restore trunk
authorTim Goya <tuxdev103@gmail.com>
Sun, 16 Dec 2007 23:13:50 +0000 (23:13 +0000)
committerTim Goya <tuxdev103@gmail.com>
Sun, 16 Dec 2007 23:13:50 +0000 (23:13 +0000)
SVN-Revision: 5207

490 files changed:
src/Jamfile [new file with mode: 0644]
src/addon.cpp [new file with mode: 0644]
src/addon.hpp [new file with mode: 0644]
src/addon_manager.cpp [new file with mode: 0644]
src/addon_manager.hpp [new file with mode: 0644]
src/audio/dummy_sound_source.cpp [new file with mode: 0644]
src/audio/dummy_sound_source.hpp [new file with mode: 0644]
src/audio/openal_sound_source.cpp [new file with mode: 0644]
src/audio/openal_sound_source.hpp [new file with mode: 0644]
src/audio/sound_file.cpp [new file with mode: 0644]
src/audio/sound_file.hpp [new file with mode: 0644]
src/audio/sound_manager.cpp [new file with mode: 0644]
src/audio/sound_manager.hpp [new file with mode: 0644]
src/audio/sound_source.hpp [new file with mode: 0644]
src/audio/stream_sound_source.cpp [new file with mode: 0644]
src/audio/stream_sound_source.hpp [new file with mode: 0644]
src/badguy/angrystone.cpp [new file with mode: 0644]
src/badguy/angrystone.hpp [new file with mode: 0644]
src/badguy/badguy.cpp [new file with mode: 0644]
src/badguy/badguy.hpp [new file with mode: 0644]
src/badguy/bomb.cpp [new file with mode: 0644]
src/badguy/bomb.hpp [new file with mode: 0644]
src/badguy/bouncing_snowball.cpp [new file with mode: 0644]
src/badguy/bouncing_snowball.hpp [new file with mode: 0644]
src/badguy/dart.cpp [new file with mode: 0644]
src/badguy/dart.hpp [new file with mode: 0644]
src/badguy/darttrap.cpp [new file with mode: 0644]
src/badguy/darttrap.hpp [new file with mode: 0644]
src/badguy/dispenser.cpp [new file with mode: 0644]
src/badguy/dispenser.hpp [new file with mode: 0644]
src/badguy/fish.cpp [new file with mode: 0644]
src/badguy/fish.hpp [new file with mode: 0644]
src/badguy/flame.cpp [new file with mode: 0644]
src/badguy/flame.hpp [new file with mode: 0644]
src/badguy/flyingsnowball.cpp [new file with mode: 0644]
src/badguy/flyingsnowball.hpp [new file with mode: 0644]
src/badguy/ghosttree.cpp [new file with mode: 0644]
src/badguy/ghosttree.hpp [new file with mode: 0644]
src/badguy/igel.cpp [new file with mode: 0644]
src/badguy/igel.hpp [new file with mode: 0644]
src/badguy/jumpy.cpp [new file with mode: 0644]
src/badguy/jumpy.hpp [new file with mode: 0644]
src/badguy/kugelblitz.cpp [new file with mode: 0644]
src/badguy/kugelblitz.hpp [new file with mode: 0644]
src/badguy/mole.cpp [new file with mode: 0644]
src/badguy/mole.hpp [new file with mode: 0644]
src/badguy/mole_rock.cpp [new file with mode: 0644]
src/badguy/mole_rock.hpp [new file with mode: 0644]
src/badguy/mrbomb.cpp [new file with mode: 0644]
src/badguy/mrbomb.hpp [new file with mode: 0644]
src/badguy/mriceblock.cpp [new file with mode: 0644]
src/badguy/mriceblock.hpp [new file with mode: 0644]
src/badguy/mrrocket.cpp [new file with mode: 0644]
src/badguy/mrrocket.hpp [new file with mode: 0644]
src/badguy/mrtree.cpp [new file with mode: 0644]
src/badguy/mrtree.hpp [new file with mode: 0644]
src/badguy/plant.cpp [new file with mode: 0644]
src/badguy/plant.hpp [new file with mode: 0644]
src/badguy/poisonivy.cpp [new file with mode: 0644]
src/badguy/poisonivy.hpp [new file with mode: 0644]
src/badguy/root.cpp [new file with mode: 0644]
src/badguy/root.hpp [new file with mode: 0644]
src/badguy/skullyhop.cpp [new file with mode: 0644]
src/badguy/skullyhop.hpp [new file with mode: 0644]
src/badguy/snail.cpp [new file with mode: 0644]
src/badguy/snail.hpp [new file with mode: 0644]
src/badguy/snowball.cpp [new file with mode: 0644]
src/badguy/snowball.hpp [new file with mode: 0644]
src/badguy/spidermite.cpp [new file with mode: 0644]
src/badguy/spidermite.hpp [new file with mode: 0644]
src/badguy/spiky.cpp [new file with mode: 0644]
src/badguy/spiky.hpp [new file with mode: 0644]
src/badguy/sspiky.cpp [new file with mode: 0644]
src/badguy/sspiky.hpp [new file with mode: 0644]
src/badguy/stalactite.cpp [new file with mode: 0644]
src/badguy/stalactite.hpp [new file with mode: 0644]
src/badguy/stumpy.cpp [new file with mode: 0644]
src/badguy/stumpy.hpp [new file with mode: 0644]
src/badguy/toad.cpp [new file with mode: 0644]
src/badguy/toad.hpp [new file with mode: 0644]
src/badguy/totem.cpp [new file with mode: 0644]
src/badguy/totem.hpp [new file with mode: 0644]
src/badguy/treewillowisp.cpp [new file with mode: 0644]
src/badguy/treewillowisp.hpp [new file with mode: 0644]
src/badguy/walking_badguy.cpp [new file with mode: 0644]
src/badguy/walking_badguy.hpp [new file with mode: 0644]
src/badguy/walkingleaf.cpp [new file with mode: 0644]
src/badguy/walkingleaf.hpp [new file with mode: 0644]
src/badguy/willowisp.cpp [new file with mode: 0644]
src/badguy/willowisp.hpp [new file with mode: 0644]
src/badguy/yeti.cpp [new file with mode: 0644]
src/badguy/yeti.hpp [new file with mode: 0644]
src/badguy/yeti_stalactite.cpp [new file with mode: 0644]
src/badguy/yeti_stalactite.hpp [new file with mode: 0644]
src/badguy/zeekling.cpp [new file with mode: 0644]
src/badguy/zeekling.hpp [new file with mode: 0644]
src/binreloc/binreloc.c [new file with mode: 0644]
src/binreloc/binreloc.h [new file with mode: 0644]
src/collision.cpp [new file with mode: 0644]
src/collision.hpp [new file with mode: 0644]
src/collision_hit.hpp [new file with mode: 0644]
src/console.cpp [new file with mode: 0644]
src/console.hpp [new file with mode: 0644]
src/control/codecontroller.cpp [new file with mode: 0644]
src/control/codecontroller.hpp [new file with mode: 0644]
src/control/controller.cpp [new file with mode: 0644]
src/control/controller.hpp [new file with mode: 0644]
src/control/joystickkeyboardcontroller.cpp [new file with mode: 0644]
src/control/joystickkeyboardcontroller.hpp [new file with mode: 0644]
src/direction.hpp [new file with mode: 0644]
src/fadeout.cpp [new file with mode: 0644]
src/fadeout.hpp [new file with mode: 0644]
src/file_system.cpp [new file with mode: 0644]
src/file_system.hpp [new file with mode: 0644]
src/flip_level_transformer.cpp [new file with mode: 0644]
src/flip_level_transformer.hpp [new file with mode: 0644]
src/game_object.cpp [new file with mode: 0644]
src/game_object.hpp [new file with mode: 0644]
src/game_session.cpp [new file with mode: 0644]
src/game_session.hpp [new file with mode: 0644]
src/gameconfig.cpp [new file with mode: 0644]
src/gameconfig.hpp [new file with mode: 0644]
src/gettext.hpp [new file with mode: 0644]
src/gui/button.cpp [new file with mode: 0644]
src/gui/button.hpp [new file with mode: 0644]
src/gui/menu.cpp [new file with mode: 0644]
src/gui/menu.hpp [new file with mode: 0644]
src/gui/mousecursor.cpp [new file with mode: 0644]
src/gui/mousecursor.hpp [new file with mode: 0644]
src/level.cpp [new file with mode: 0644]
src/level.hpp [new file with mode: 0644]
src/level_transformer.cpp [new file with mode: 0644]
src/level_transformer.hpp [new file with mode: 0644]
src/lisp/lexer.cpp [new file with mode: 0644]
src/lisp/lexer.hpp [new file with mode: 0644]
src/lisp/lisp.cpp [new file with mode: 0644]
src/lisp/lisp.hpp [new file with mode: 0644]
src/lisp/list_iterator.cpp [new file with mode: 0644]
src/lisp/list_iterator.hpp [new file with mode: 0644]
src/lisp/parser.cpp [new file with mode: 0644]
src/lisp/parser.hpp [new file with mode: 0644]
src/lisp/writer.cpp [new file with mode: 0644]
src/lisp/writer.hpp [new file with mode: 0644]
src/log.cpp [new file with mode: 0644]
src/log.hpp [new file with mode: 0644]
src/main.cpp [new file with mode: 0644]
src/main.hpp [new file with mode: 0644]
src/mainloop.cpp [new file with mode: 0644]
src/mainloop.hpp [new file with mode: 0644]
src/math/aatriangle.hpp [new file with mode: 0644]
src/math/rect.hpp [new file with mode: 0644]
src/math/vector.cpp [new file with mode: 0644]
src/math/vector.hpp [new file with mode: 0644]
src/moving_object.cpp [new file with mode: 0644]
src/moving_object.hpp [new file with mode: 0644]
src/object/ambient_sound.cpp [new file with mode: 0644]
src/object/ambient_sound.hpp [new file with mode: 0644]
src/object/anchor_point.cpp [new file with mode: 0644]
src/object/anchor_point.hpp [new file with mode: 0644]
src/object/background.cpp [new file with mode: 0644]
src/object/background.hpp [new file with mode: 0644]
src/object/bicycle_platform.cpp [new file with mode: 0644]
src/object/bicycle_platform.hpp [new file with mode: 0644]
src/object/block.cpp [new file with mode: 0644]
src/object/block.hpp [new file with mode: 0644]
src/object/bullet.cpp [new file with mode: 0644]
src/object/bullet.hpp [new file with mode: 0644]
src/object/camera.cpp [new file with mode: 0644]
src/object/camera.hpp [new file with mode: 0644]
src/object/candle.cpp [new file with mode: 0644]
src/object/candle.hpp [new file with mode: 0644]
src/object/coin.cpp [new file with mode: 0644]
src/object/coin.hpp [new file with mode: 0644]
src/object/display_effect.cpp [new file with mode: 0644]
src/object/display_effect.hpp [new file with mode: 0644]
src/object/electrifier.cpp [new file with mode: 0644]
src/object/electrifier.hpp [new file with mode: 0644]
src/object/endsequence.cpp [new file with mode: 0644]
src/object/endsequence.hpp [new file with mode: 0644]
src/object/endsequence_fireworks.cpp [new file with mode: 0644]
src/object/endsequence_fireworks.hpp [new file with mode: 0644]
src/object/endsequence_walkleft.cpp [new file with mode: 0644]
src/object/endsequence_walkleft.hpp [new file with mode: 0644]
src/object/endsequence_walkright.cpp [new file with mode: 0644]
src/object/endsequence_walkright.hpp [new file with mode: 0644]
src/object/explosion.cpp [new file with mode: 0644]
src/object/explosion.hpp [new file with mode: 0644]
src/object/falling_coin.cpp [new file with mode: 0644]
src/object/falling_coin.hpp [new file with mode: 0644]
src/object/firefly.cpp [new file with mode: 0644]
src/object/firefly.hpp [new file with mode: 0644]
src/object/fireworks.cpp [new file with mode: 0644]
src/object/fireworks.hpp [new file with mode: 0644]
src/object/floating_image.cpp [new file with mode: 0644]
src/object/floating_image.hpp [new file with mode: 0644]
src/object/flower.cpp [new file with mode: 0644]
src/object/flower.hpp [new file with mode: 0644]
src/object/gameobjs.cpp [new file with mode: 0644]
src/object/gameobjs.hpp [new file with mode: 0644]
src/object/gradient.cpp [new file with mode: 0644]
src/object/gradient.hpp [new file with mode: 0644]
src/object/growup.cpp [new file with mode: 0644]
src/object/growup.hpp [new file with mode: 0644]
src/object/hurting_platform.cpp [new file with mode: 0644]
src/object/hurting_platform.hpp [new file with mode: 0644]
src/object/infoblock.cpp [new file with mode: 0644]
src/object/infoblock.hpp [new file with mode: 0644]
src/object/invisible_block.cpp [new file with mode: 0644]
src/object/invisible_block.hpp [new file with mode: 0644]
src/object/invisible_wall.cpp [new file with mode: 0644]
src/object/invisible_wall.hpp [new file with mode: 0644]
src/object/ispy.cpp [new file with mode: 0644]
src/object/ispy.hpp [new file with mode: 0644]
src/object/lantern.cpp [new file with mode: 0644]
src/object/lantern.hpp [new file with mode: 0644]
src/object/level_time.cpp [new file with mode: 0644]
src/object/level_time.hpp [new file with mode: 0644]
src/object/light.cpp [new file with mode: 0644]
src/object/light.hpp [new file with mode: 0644]
src/object/magicblock.cpp [new file with mode: 0644]
src/object/magicblock.hpp [new file with mode: 0644]
src/object/moving_sprite.cpp [new file with mode: 0644]
src/object/moving_sprite.hpp [new file with mode: 0644]
src/object/oneup.cpp [new file with mode: 0644]
src/object/oneup.hpp [new file with mode: 0644]
src/object/particles.cpp [new file with mode: 0644]
src/object/particles.hpp [new file with mode: 0644]
src/object/particlesystem.cpp [new file with mode: 0644]
src/object/particlesystem.hpp [new file with mode: 0644]
src/object/particlesystem_interactive.cpp [new file with mode: 0644]
src/object/particlesystem_interactive.hpp [new file with mode: 0644]
src/object/path.cpp [new file with mode: 0644]
src/object/path.hpp [new file with mode: 0644]
src/object/path_walker.cpp [new file with mode: 0644]
src/object/path_walker.hpp [new file with mode: 0644]
src/object/platform.cpp [new file with mode: 0644]
src/object/platform.hpp [new file with mode: 0644]
src/object/player.cpp [new file with mode: 0644]
src/object/player.hpp [new file with mode: 0644]
src/object/pneumatic_platform.cpp [new file with mode: 0644]
src/object/pneumatic_platform.hpp [new file with mode: 0644]
src/object/portable.hpp [new file with mode: 0644]
src/object/powerup.cpp [new file with mode: 0644]
src/object/powerup.hpp [new file with mode: 0644]
src/object/pulsing_light.cpp [new file with mode: 0644]
src/object/pulsing_light.hpp [new file with mode: 0644]
src/object/pushbutton.cpp [new file with mode: 0644]
src/object/pushbutton.hpp [new file with mode: 0644]
src/object/rainsplash.cpp [new file with mode: 0644]
src/object/rainsplash.hpp [new file with mode: 0644]
src/object/rock.cpp [new file with mode: 0644]
src/object/rock.hpp [new file with mode: 0644]
src/object/scripted_object.cpp [new file with mode: 0644]
src/object/scripted_object.hpp [new file with mode: 0644]
src/object/skull_tile.cpp [new file with mode: 0644]
src/object/skull_tile.hpp [new file with mode: 0644]
src/object/specialriser.cpp [new file with mode: 0644]
src/object/specialriser.hpp [new file with mode: 0644]
src/object/spotlight.cpp [new file with mode: 0644]
src/object/spotlight.hpp [new file with mode: 0644]
src/object/sprite_particle.cpp [new file with mode: 0644]
src/object/sprite_particle.hpp [new file with mode: 0644]
src/object/star.cpp [new file with mode: 0644]
src/object/star.hpp [new file with mode: 0644]
src/object/text_object.cpp [new file with mode: 0644]
src/object/text_object.hpp [new file with mode: 0644]
src/object/thunderstorm.cpp [new file with mode: 0644]
src/object/thunderstorm.hpp [new file with mode: 0644]
src/object/tilemap.cpp [new file with mode: 0644]
src/object/tilemap.hpp [new file with mode: 0644]
src/object/trampoline.cpp [new file with mode: 0644]
src/object/trampoline.hpp [new file with mode: 0644]
src/object/unstable_tile.cpp [new file with mode: 0644]
src/object/unstable_tile.hpp [new file with mode: 0644]
src/object/weak_block.cpp [new file with mode: 0644]
src/object/weak_block.hpp [new file with mode: 0644]
src/object/wind.cpp [new file with mode: 0644]
src/object/wind.hpp [new file with mode: 0644]
src/object_factory.cpp [new file with mode: 0644]
src/object_factory.hpp [new file with mode: 0644]
src/object_remove_listener.hpp [new file with mode: 0644]
src/obstack/obstack.c [new file with mode: 0644]
src/obstack/obstack.h [new file with mode: 0644]
src/obstack/obstackpp.hpp [new file with mode: 0644]
src/options_menu.cpp [new file with mode: 0644]
src/options_menu.hpp [new file with mode: 0644]
src/physfs/physfs_sdl.cpp [new file with mode: 0644]
src/physfs/physfs_sdl.hpp [new file with mode: 0644]
src/physfs/physfs_stream.cpp [new file with mode: 0644]
src/physfs/physfs_stream.hpp [new file with mode: 0644]
src/physic.cpp [new file with mode: 0644]
src/physic.hpp [new file with mode: 0644]
src/player_status.cpp [new file with mode: 0644]
src/player_status.hpp [new file with mode: 0644]
src/random_generator.cpp [new file with mode: 0644]
src/random_generator.hpp [new file with mode: 0644]
src/ref.hpp [new file with mode: 0644]
src/refcounter.hpp [new file with mode: 0644]
src/resources.cpp [new file with mode: 0644]
src/resources.hpp [new file with mode: 0644]
src/screen.hpp [new file with mode: 0644]
src/screen_fade.hpp [new file with mode: 0644]
src/script_interface.hpp [new file with mode: 0644]
src/scripting/Jamfile [new file with mode: 0644]
src/scripting/ambient_sound.hpp [new file with mode: 0644]
src/scripting/anchor_points.hpp [new file with mode: 0644]
src/scripting/camera.cpp [new file with mode: 0644]
src/scripting/camera.hpp [new file with mode: 0644]
src/scripting/candle.cpp [new file with mode: 0644]
src/scripting/candle.hpp [new file with mode: 0644]
src/scripting/display_effect.hpp [new file with mode: 0644]
src/scripting/floating_image.cpp [new file with mode: 0644]
src/scripting/floating_image.hpp [new file with mode: 0644]
src/scripting/functions.cpp [new file with mode: 0644]
src/scripting/functions.hpp [new file with mode: 0644]
src/scripting/level.cpp [new file with mode: 0644]
src/scripting/level.hpp [new file with mode: 0644]
src/scripting/level_time.cpp [new file with mode: 0644]
src/scripting/level_time.hpp [new file with mode: 0644]
src/scripting/platform.cpp [new file with mode: 0644]
src/scripting/platform.hpp [new file with mode: 0644]
src/scripting/player.hpp [new file with mode: 0644]
src/scripting/scripted_object.hpp [new file with mode: 0644]
src/scripting/serialize.cpp [new file with mode: 0644]
src/scripting/serialize.hpp [new file with mode: 0644]
src/scripting/squirrel_error.cpp [new file with mode: 0644]
src/scripting/squirrel_error.hpp [new file with mode: 0644]
src/scripting/squirrel_util.cpp [new file with mode: 0644]
src/scripting/squirrel_util.hpp [new file with mode: 0644]
src/scripting/ssector.hpp [new file with mode: 0644]
src/scripting/text.hpp [new file with mode: 0644]
src/scripting/thread_queue.cpp [new file with mode: 0644]
src/scripting/thread_queue.hpp [new file with mode: 0644]
src/scripting/thunderstorm.cpp [new file with mode: 0644]
src/scripting/thunderstorm.hpp [new file with mode: 0644]
src/scripting/tilemap.cpp [new file with mode: 0644]
src/scripting/tilemap.hpp [new file with mode: 0644]
src/scripting/time_scheduler.cpp [new file with mode: 0644]
src/scripting/time_scheduler.hpp [new file with mode: 0644]
src/scripting/willowisp.hpp [new file with mode: 0644]
src/scripting/wind.cpp [new file with mode: 0644]
src/scripting/wind.hpp [new file with mode: 0644]
src/scripting/wrapper.cpp [new file with mode: 0644]
src/scripting/wrapper.hpp [new file with mode: 0644]
src/scripting/wrapper.interface.hpp [new file with mode: 0644]
src/sector.cpp [new file with mode: 0644]
src/sector.hpp [new file with mode: 0644]
src/serializable.hpp [new file with mode: 0644]
src/shrinkfade.cpp [new file with mode: 0644]
src/shrinkfade.hpp [new file with mode: 0644]
src/spawn_point.cpp [new file with mode: 0644]
src/spawn_point.hpp [new file with mode: 0644]
src/sprite/sprite.cpp [new file with mode: 0644]
src/sprite/sprite.hpp [new file with mode: 0644]
src/sprite/sprite_data.cpp [new file with mode: 0644]
src/sprite/sprite_data.hpp [new file with mode: 0644]
src/sprite/sprite_manager.cpp [new file with mode: 0644]
src/sprite/sprite_manager.hpp [new file with mode: 0644]
src/squirrel/CMakeLists.txt [new file with mode: 0644]
src/squirrel/COPYRIGHT [new file with mode: 0644]
src/squirrel/HISTORY [new file with mode: 0644]
src/squirrel/Jamfile [new file with mode: 0644]
src/squirrel/README [new file with mode: 0644]
src/squirrel/include/sqstdaux.h [new file with mode: 0644]
src/squirrel/include/sqstdblob.h [new file with mode: 0644]
src/squirrel/include/sqstdio.h [new file with mode: 0644]
src/squirrel/include/sqstdmath.h [new file with mode: 0644]
src/squirrel/include/sqstdstring.h [new file with mode: 0644]
src/squirrel/include/sqstdsystem.h [new file with mode: 0644]
src/squirrel/include/squirrel.h [new file with mode: 0644]
src/squirrel/sqdbg/serialize_state.inl [new file with mode: 0644]
src/squirrel/sqdbg/sqdbgserver.cpp [new file with mode: 0644]
src/squirrel/sqdbg/sqdbgserver.h [new file with mode: 0644]
src/squirrel/sqdbg/sqrdbg.cpp [new file with mode: 0644]
src/squirrel/sqdbg/sqrdbg.h [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdaux.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdblob.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdblobimpl.h [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdio.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdmath.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdrex.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdstream.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdstream.h [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdstring.cpp [new file with mode: 0644]
src/squirrel/sqstdlib/sqstdsystem.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqapi.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqarray.h [new file with mode: 0644]
src/squirrel/squirrel/sqbaselib.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqclass.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqclass.h [new file with mode: 0644]
src/squirrel/squirrel/sqclosure.h [new file with mode: 0644]
src/squirrel/squirrel/sqcompiler.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqcompiler.h [new file with mode: 0644]
src/squirrel/squirrel/sqdebug.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqfuncproto.h [new file with mode: 0644]
src/squirrel/squirrel/sqfuncstate.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqfuncstate.h [new file with mode: 0644]
src/squirrel/squirrel/sqlexer.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqlexer.h [new file with mode: 0644]
src/squirrel/squirrel/sqmem.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqobject.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqobject.h [new file with mode: 0644]
src/squirrel/squirrel/sqopcodes.h [new file with mode: 0644]
src/squirrel/squirrel/sqpcheader.h [new file with mode: 0644]
src/squirrel/squirrel/sqstate.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqstate.h [new file with mode: 0644]
src/squirrel/squirrel/sqstring.h [new file with mode: 0644]
src/squirrel/squirrel/sqtable.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqtable.h [new file with mode: 0644]
src/squirrel/squirrel/squserdata.h [new file with mode: 0644]
src/squirrel/squirrel/squtils.h [new file with mode: 0644]
src/squirrel/squirrel/sqvm.cpp [new file with mode: 0644]
src/squirrel/squirrel/sqvm.h [new file with mode: 0644]
src/statistics.cpp [new file with mode: 0644]
src/statistics.hpp [new file with mode: 0644]
src/textscroller.cpp [new file with mode: 0644]
src/textscroller.hpp [new file with mode: 0644]
src/tile.cpp [new file with mode: 0644]
src/tile.hpp [new file with mode: 0644]
src/tile_manager.cpp [new file with mode: 0644]
src/tile_manager.hpp [new file with mode: 0644]
src/timer.cpp [new file with mode: 0644]
src/timer.hpp [new file with mode: 0644]
src/tinygettext/findlocale.cpp [new file with mode: 0644]
src/tinygettext/findlocale.hpp [new file with mode: 0644]
src/tinygettext/tinygettext.cpp [new file with mode: 0644]
src/tinygettext/tinygettext.hpp [new file with mode: 0644]
src/title.cpp [new file with mode: 0644]
src/title.hpp [new file with mode: 0644]
src/trigger/climbable.cpp [new file with mode: 0644]
src/trigger/climbable.hpp [new file with mode: 0644]
src/trigger/door.cpp [new file with mode: 0644]
src/trigger/door.hpp [new file with mode: 0644]
src/trigger/scripttrigger.cpp [new file with mode: 0644]
src/trigger/scripttrigger.hpp [new file with mode: 0644]
src/trigger/secretarea_trigger.cpp [new file with mode: 0644]
src/trigger/secretarea_trigger.hpp [new file with mode: 0644]
src/trigger/sequence_trigger.cpp [new file with mode: 0644]
src/trigger/sequence_trigger.hpp [new file with mode: 0644]
src/trigger/switch.cpp [new file with mode: 0644]
src/trigger/switch.hpp [new file with mode: 0644]
src/trigger/trigger_base.cpp [new file with mode: 0644]
src/trigger/trigger_base.hpp [new file with mode: 0644]
src/video/color.cpp [new file with mode: 0644]
src/video/color.hpp [new file with mode: 0644]
src/video/drawing_context.cpp [new file with mode: 0644]
src/video/drawing_context.hpp [new file with mode: 0644]
src/video/drawing_request.hpp [new file with mode: 0644]
src/video/font.cpp [new file with mode: 0644]
src/video/font.hpp [new file with mode: 0644]
src/video/gl_lightmap.cpp [new file with mode: 0644]
src/video/gl_lightmap.hpp [new file with mode: 0644]
src/video/gl_renderer.cpp [new file with mode: 0644]
src/video/gl_renderer.hpp [new file with mode: 0644]
src/video/gl_surface_data.hpp [new file with mode: 0644]
src/video/gl_texture.cpp [new file with mode: 0644]
src/video/gl_texture.hpp [new file with mode: 0644]
src/video/glutil.hpp [new file with mode: 0644]
src/video/lightmap.hpp [new file with mode: 0644]
src/video/renderer.hpp [new file with mode: 0644]
src/video/sdl_lightmap.cpp [new file with mode: 0644]
src/video/sdl_lightmap.hpp [new file with mode: 0644]
src/video/sdl_renderer.cpp [new file with mode: 0644]
src/video/sdl_renderer.hpp [new file with mode: 0644]
src/video/sdl_surface_data.hpp [new file with mode: 0644]
src/video/sdl_texture.cpp [new file with mode: 0644]
src/video/sdl_texture.hpp [new file with mode: 0644]
src/video/surface.hpp [new file with mode: 0644]
src/video/texture.hpp [new file with mode: 0644]
src/video/texture_manager.cpp [new file with mode: 0644]
src/video/texture_manager.hpp [new file with mode: 0644]
src/video/video_systems.cpp [new file with mode: 0644]
src/video/video_systems.hpp [new file with mode: 0644]
src/world.cpp [new file with mode: 0644]
src/world.hpp [new file with mode: 0644]
src/worldmap/direction.hpp [new file with mode: 0644]
src/worldmap/level.cpp [new file with mode: 0644]
src/worldmap/level.hpp [new file with mode: 0644]
src/worldmap/spawn_point.cpp [new file with mode: 0644]
src/worldmap/spawn_point.hpp [new file with mode: 0644]
src/worldmap/special_tile.cpp [new file with mode: 0644]
src/worldmap/special_tile.hpp [new file with mode: 0644]
src/worldmap/sprite_change.cpp [new file with mode: 0644]
src/worldmap/sprite_change.hpp [new file with mode: 0644]
src/worldmap/teleporter.cpp [new file with mode: 0644]
src/worldmap/teleporter.hpp [new file with mode: 0644]
src/worldmap/tux.cpp [new file with mode: 0644]
src/worldmap/tux.hpp [new file with mode: 0644]
src/worldmap/worldmap.cpp [new file with mode: 0644]
src/worldmap/worldmap.hpp [new file with mode: 0644]

diff --git a/src/Jamfile b/src/Jamfile
new file mode 100644 (file)
index 0000000..092591a
--- /dev/null
@@ -0,0 +1,35 @@
+SubDir TOP src ;
+
+SubInclude TOP src squirrel ;
+SubInclude TOP src scripting ;
+
+sources =
+    [ Wildcard *.cpp *.hpp ]
+    [ Wildcard audio : *.cpp *.hpp ]
+    [ Wildcard audio/newapi : *.cpp *.hpp ]
+    [ Wildcard badguy : *.cpp *.hpp ]
+    [ Wildcard binreloc : *.c *.h ]
+    [ Wildcard control : *.cpp *.hpp ]
+    [ Wildcard gui : *.cpp *.hpp ]
+    [ Wildcard lisp : *.cpp *.hpp ]
+    [ Wildcard math : *.cpp *.hpp ]
+    [ Wildcard object : *.cpp *.hpp ]
+    [ Wildcard physfs : *.cpp *.hpp ]
+    [ Wildcard sprite : *.cpp *.hpp ]
+    [ Wildcard tinygettext : *.cpp *.hpp ]
+    [ Wildcard trigger : *.cpp *.hpp ]
+    [ Wildcard video : *.cpp *.hpp ]
+    [ Wildcard worldmap : *.cpp *.hpp ]
+       [ Wildcard obstack : *.c *.h *.hpp ]
+;
+TRANSLATABLE_SOURCES += [ SearchSource $(sources) ] ;
+
+#Application supertux : $(sources) $(wrapper_objects) ;
+Application supertux2 : $(sources) $(wrapper_objects) : linkerfile ;
+C++Flags supertux2 : -DAPPDATADIR=\'\"$(appdatadir)\"\' ;
+LinkWith supertux2 : squirrel ;
+ExternalLibs supertux2 : SDL SDLIMAGE GL OPENAL VORBIS VORBISFILE OGG ICONV PHYSFS BINRELOC LIBCURL ;
+Help supertux2 : "Build the supertux2 executable" ;
+IncludeDir supertux2 : squirrel/include squirrel ;
+Package [ Wildcard scripting : *.cpp *.hpp ] ;
+
diff --git a/src/addon.cpp b/src/addon.cpp
new file mode 100644 (file)
index 0000000..1b6dbdc
--- /dev/null
@@ -0,0 +1,102 @@
+//  $Id$
+//
+//  SuperTux - Add-on
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+//
+
+#include <sstream>
+#include <stdexcept>
+#include "addon.hpp"
+#include "addon_manager.hpp"
+
+void
+Addon::install()
+{
+  AddonManager& adm = AddonManager::get_instance();
+  adm.install(*this);
+}
+
+void
+Addon::remove()
+{
+  AddonManager& adm = AddonManager::get_instance();
+  adm.remove(*this);
+}
+  
+void
+Addon::parse(const lisp::Lisp& lisp)
+{
+  try {
+    lisp.get("kind", kind);  
+    lisp.get("title", title);
+    lisp.get("author", author);
+    lisp.get("license", license);
+    lisp.get("http-url", http_url);
+    lisp.get("file", file);
+    lisp.get("md5", md5);
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when parsing addoninfo: " << e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
+
+void
+Addon::parse(std::string fname)
+{
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(fname);
+    const lisp::Lisp* addon = root->get_lisp("supertux-addoninfo");
+    if(!addon) throw std::runtime_error("file is not a supertux-addoninfo file.");
+    parse(*addon);
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when reading addoninfo '" << fname << "': " << e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
+
+void
+Addon::write(lisp::Writer& writer) const
+{
+  writer.start_list("supertux-addoninfo");
+  if (kind != "") writer.write_string("kind", kind);  
+  if (title != "") writer.write_string("title", title);
+  if (author != "") writer.write_string("author", author);
+  if (license != "") writer.write_string("license", license);
+  if (http_url != "") writer.write_string("http-url", http_url);
+  if (file != "") writer.write_string("file", file);
+  if (md5 != "") writer.write_string("md5", md5);
+  writer.end_list("supertux-addoninfo");
+}
+
+void 
+Addon::write(std::string fname) const
+{
+  lisp::Writer writer(fname);
+  write(writer);
+}
+
+bool 
+Addon::equals(const Addon& addon2) const
+{
+  if ((this->md5 == "") || (addon2.md5 == "")) return (this->title == addon2.title);
+  return (this->md5 == addon2.md5);
+}
+
diff --git a/src/addon.hpp b/src/addon.hpp
new file mode 100644 (file)
index 0000000..b65efe7
--- /dev/null
@@ -0,0 +1,84 @@
+//  $Id$
+//
+//  SuperTux - Add-on
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+//
+#ifndef ADDON_H
+#define ADDON_H
+
+#include <string>
+#include <vector>
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+
+/**
+ * Represents an (available or installed) Add-on, e.g. a level set
+ */
+class Addon
+{
+public:
+  std::string kind;
+  std::string title;
+  std::string author;
+  std::string license;
+  std::string http_url;
+  std::string file;
+  std::string md5;
+
+  bool isInstalled;
+
+  /**
+   * Download and install Add-on
+   */
+  void install();
+
+  /**
+   * Physically delete Add-on
+   */
+  void remove();
+
+  /**
+   * Read additional information from given contents of a (supertux-addoninfo ...) block
+   */
+  void parse(const lisp::Lisp& lisp);
+
+  /**
+   * Read additional information from given file
+   */
+  void parse(std::string fname);
+
+  /**
+   * Writes out Add-on metainformation to a Lisp Writer
+   */
+  void write(lisp::Writer& writer) const;
+
+  /**
+   * Writes out Add-on metainformation to a file
+   */
+  void write(std::string fname) const;
+
+  /**
+   * Checks if Add-on is the same as given one. 
+   * If available, checks MD5 sum, else relies on title alone.
+   */
+  bool equals(const Addon& addon2) const;
+
+};
+
+#endif
diff --git a/src/addon_manager.cpp b/src/addon_manager.cpp
new file mode 100644 (file)
index 0000000..79e3537
--- /dev/null
@@ -0,0 +1,295 @@
+//  $Id$
+//
+//  SuperTux - Add-on Manager
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+//
+
+#include <sstream>
+#include <stdexcept>
+#include <list>
+#include <physfs.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include "addon_manager.hpp"
+#include "config.h"
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+#endif
+
+#ifdef HAVE_LIBCURL
+namespace {
+
+  size_t my_curl_string_append(void *ptr, size_t size, size_t nmemb, void *string_ptr)
+  {
+    std::string& s = *static_cast<std::string*>(string_ptr);
+    std::string buf(static_cast<char*>(ptr), size * nmemb);
+    s += buf;
+    log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
+    return size * nmemb;
+  }
+
+  size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
+  {
+    PHYSFS_file* f = static_cast<PHYSFS_file*>(f_p);
+    PHYSFS_sint64 written = PHYSFS_write(f, ptr, size, nmemb);
+    log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
+    return size * written;
+  }
+
+}
+#endif
+
+AddonManager&
+AddonManager::get_instance()
+{
+  static AddonManager instance;
+  return instance;
+}
+
+AddonManager::AddonManager()
+{
+#ifdef HAVE_LIBCURL
+  curl_global_init(CURL_GLOBAL_ALL);
+#endif
+}
+
+AddonManager::~AddonManager()
+{
+#ifdef HAVE_LIBCURL
+  curl_global_cleanup();
+#endif
+}
+
+std::vector<Addon>
+AddonManager::get_installed_addons() const
+{
+  std::vector<Addon> addons;
+
+  // iterate over complete search path (i.e. directories and archives)
+  char **i = PHYSFS_getSearchPath();
+  if (!i) throw std::runtime_error("Could not query physfs search path");
+  for (; *i != NULL; i++) {
+
+    // get filename of potential archive
+    std::string fileName = *i;
+
+    // make sure it's in the writeDir
+    static const std::string writeDir = PHYSFS_getWriteDir();
+    if (fileName.compare(0, writeDir.length(), writeDir) != 0) continue;
+
+    // make sure it looks like an archive
+    static const std::string archiveExt = ".zip";
+    if (fileName.compare(fileName.length()-archiveExt.length(), archiveExt.length(), archiveExt) != 0) continue;
+
+    // make sure it exists
+    struct stat stats;
+    if (stat(fileName.c_str(), &stats) != 0) continue;
+
+    // make sure it's an actual file
+    if (!S_ISREG(stats.st_mode)) continue;
+
+    Addon addon;
+
+    // extract nice title as fallback for when the Add-on has no addoninfo file
+    static const char* dirSep = PHYSFS_getDirSeparator();
+    std::string::size_type n = fileName.rfind(dirSep) + 1;
+    if (n == std::string::npos) n = 0;
+    addon.title = fileName.substr(n, fileName.length() - n - archiveExt.length());
+    std::string shortFileName = fileName.substr(n, fileName.length() - n);
+    addon.file = shortFileName;
+   
+    // read an accompaining .nfo file, if it exists
+    static const std::string infoExt = ".nfo";
+    std::string infoFileName = fileName.substr(n, fileName.length() - n - archiveExt.length()) + infoExt;
+    if (PHYSFS_exists(infoFileName.c_str())) {
+      addon.parse(infoFileName);
+      if (addon.file != shortFileName) {
+        log_warning << "Add-on \"" << addon.title << "\", contained in file \"" << shortFileName << "\" is accompained by an addoninfo file that specifies \"" << addon.file << "\" as the Add-on's file name. Skipping." << std::endl;
+      }
+    }
+
+    // make sure the Add-on's file name does not contain weird characters
+    if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+      log_warning << "Add-on \"" << addon.title << "\" contains unsafe file name. Skipping." << std::endl;
+      continue;
+    }
+
+    addon.isInstalled = true;
+    addons.push_back(addon);
+  }
+
+  return addons;
+}
+
+std::vector<Addon>
+AddonManager::get_available_addons() const
+{
+  std::vector<Addon> addons;
+
+#ifdef HAVE_LIBCURL
+
+  char error_buffer[CURL_ERROR_SIZE+1];
+
+  const char* baseUrl = "http://supertux.lethargik.org/addons/index.nfo";
+  std::string addoninfos = "";
+
+  CURL *curl_handle;
+  curl_handle = curl_easy_init();
+  curl_easy_setopt(curl_handle, CURLOPT_URL, baseUrl);
+  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
+  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_string_append);
+  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &addoninfos);
+  curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
+  curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
+  curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
+  CURLcode result = curl_easy_perform(curl_handle);
+  curl_easy_cleanup(curl_handle);
+
+  if (result != CURLE_OK) {
+    std::string why = error_buffer[0] ? error_buffer : "unhandled error";
+    throw std::runtime_error("Downloading Add-on list failed: " + why);
+  }
+
+  try {
+    lisp::Parser parser;
+    std::stringstream addoninfos_stream(addoninfos);
+    const lisp::Lisp* root = parser.parse(addoninfos_stream, "supertux-addons");
+
+    const lisp::Lisp* addons_lisp = root->get_lisp("supertux-addons");
+    if(!addons_lisp) throw std::runtime_error("Downloaded file is not an Add-on list");
+
+    lisp::ListIterator iter(addons_lisp);
+    while(iter.next()) {
+      const std::string& token = iter.item();
+      if(token == "supertux-addoninfo") {
+        Addon addon;
+       addon.parse(*(iter.lisp()));
+
+        // make sure the Add-on's file name does not contain weird characters
+        if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+          log_warning << "Add-on \"" << addon.title << "\" contains unsafe file name. Skipping." << std::endl;
+          continue;
+        }
+
+        addon.isInstalled = false;
+        addons.push_back(addon);
+      } else {
+        log_warning << "Unknown token '" << token << "' in Add-on list" << std::endl;
+      }
+    }
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when reading Add-on list: " << e.what();
+    throw std::runtime_error(msg.str());
+  }
+
+#endif
+
+  return addons;
+}
+
+
+void
+AddonManager::install(const Addon& addon)
+{
+  // make sure the Add-on's file name does not contain weird characters
+  if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+    throw std::runtime_error("Add-on has unsafe file name (\""+addon.file+"\")");
+  }
+
+#ifdef HAVE_LIBCURL
+
+  char error_buffer[CURL_ERROR_SIZE+1];
+
+  char* url = (char*)malloc(addon.http_url.length() + 1);
+  strncpy(url, addon.http_url.c_str(), addon.http_url.length() + 1);
+
+  PHYSFS_file* f = PHYSFS_openWrite(addon.file.c_str());
+
+  log_debug << "Downloading \"" << url << "\"" << std::endl;
+
+  CURL *curl_handle;
+  curl_handle = curl_easy_init();
+  curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
+  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_physfs_write);
+  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);
+  curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
+  curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
+  curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
+  CURLcode result = curl_easy_perform(curl_handle);
+  curl_easy_cleanup(curl_handle);
+
+  PHYSFS_close(f);
+
+  free(url);
+
+  if (result != CURLE_OK) {
+    PHYSFS_delete(addon.file.c_str());
+    std::string why = error_buffer[0] ? error_buffer : "unhandled error";
+    throw std::runtime_error("Downloading Add-on failed: " + why);
+  }
+
+  // write an accompaining .nfo file
+  static const std::string archiveExt = ".zip";
+  static const std::string infoExt = ".nfo";
+  std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
+  addon.write(infoFileName);
+
+  static const std::string writeDir = PHYSFS_getWriteDir();
+  static const std::string dirSep = PHYSFS_getDirSeparator();
+  std::string fullFilename = writeDir + dirSep + addon.file;
+  log_debug << "Finished downloading \"" << fullFilename << "\"" << std::endl;
+  PHYSFS_addToSearchPath(fullFilename.c_str(), 1);
+#else
+  (void) addon;
+#endif
+
+}
+
+void
+AddonManager::remove(const Addon& addon)
+{
+  // make sure the Add-on's file name does not contain weird characters
+  if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+    throw std::runtime_error("Add-on has unsafe file name (\""+addon.file+"\")");
+  }
+
+  log_debug << "deleting file \"" << addon.file << "\"" << std::endl;
+  PHYSFS_removeFromSearchPath(addon.file.c_str());
+  PHYSFS_delete(addon.file.c_str());
+
+  // remove an accompaining .nfo file
+  static const std::string archiveExt = ".zip";
+  static const std::string infoExt = ".nfo";
+  std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
+  if (PHYSFS_exists(infoFileName.c_str())) {
+    log_debug << "deleting file \"" << infoFileName << "\"" << std::endl;
+    PHYSFS_delete(infoFileName.c_str());
+  }
+}
+
diff --git a/src/addon_manager.hpp b/src/addon_manager.hpp
new file mode 100644 (file)
index 0000000..23b3415
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux - Add-on Manager
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+//
+#ifndef ADDON_MANAGER_H
+#define ADDON_MANAGER_H
+
+#include <string>
+#include <vector>
+#include "addon.hpp"
+
+/**
+ * Checks for, installs and removes Add-ons
+ */
+class AddonManager
+{
+public:
+  /**
+   * returns a list of installed Add-ons
+   */
+  std::vector<Addon> get_installed_addons() const;
+  
+  /**
+   * returns a list of available Add-ons
+   */
+  std::vector<Addon> get_available_addons() const;
+
+  /**
+   * Download and install Add-on
+   */
+  void install(const Addon& addon);
+
+  /**
+   * Physically delete Add-on
+   */
+  void remove(const Addon& addon);
+
+  static AddonManager& get_instance();
+
+protected:
+  AddonManager();
+  ~AddonManager();
+};
+
+#endif
diff --git a/src/audio/dummy_sound_source.cpp b/src/audio/dummy_sound_source.cpp
new file mode 100644 (file)
index 0000000..1a9c274
--- /dev/null
@@ -0,0 +1,81 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "dummy_sound_source.hpp"
+
+class DummySoundSource : public SoundSource
+{
+public:
+  DummySoundSource()
+  {}
+  virtual ~DummySoundSource()
+  {}
+
+  virtual void play()
+  {
+    is_playing = true;
+  }
+
+  virtual void stop()
+  {
+    is_playing = false;
+  }
+
+  virtual bool playing()
+  {
+    return is_playing;
+  }
+
+  virtual void set_looping(bool )
+  {
+  }
+
+  virtual void set_gain(float )
+  {
+  }
+
+  virtual void set_pitch(float )
+  {
+  }
+
+  virtual void set_position(const Vector& )
+  {
+  }
+
+  virtual void set_velocity(const Vector& )
+  {
+  }
+
+  virtual void set_reference_distance(float )
+  {
+  }
+
+  virtual void set_rollof_factor(float )
+  {
+  }
+
+private:
+  bool is_playing;
+};
+
+SoundSource* create_dummy_sound_source()
+{
+  return new DummySoundSource();
+}
diff --git a/src/audio/dummy_sound_source.hpp b/src/audio/dummy_sound_source.hpp
new file mode 100644 (file)
index 0000000..458c1e8
--- /dev/null
@@ -0,0 +1,26 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __DUMMY_SOUND_SOURCE_HPP__
+#define __DUMMY_SOUND_SOURCE_HPP__
+
+#include "sound_source.hpp"
+
+SoundSource* create_dummy_sound_source();
+
+#endif
diff --git a/src/audio/openal_sound_source.cpp b/src/audio/openal_sound_source.cpp
new file mode 100644 (file)
index 0000000..252cc6e
--- /dev/null
@@ -0,0 +1,105 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "openal_sound_source.hpp"
+#include "sound_manager.hpp"
+
+OpenALSoundSource::OpenALSoundSource()
+{
+  alGenSources(1, &source);
+  SoundManager::check_al_error("Couldn't create audio source: ");
+  set_reference_distance(128);
+}
+
+OpenALSoundSource::~OpenALSoundSource()
+{
+  stop();
+  alDeleteSources(1, &source);
+}
+
+void
+OpenALSoundSource::stop()
+{
+  alSourceStop(source);
+  alSourcei(source, AL_BUFFER, AL_NONE);
+  SoundManager::check_al_error("Problem stopping audio source: ");
+}
+
+void
+OpenALSoundSource::play()
+{
+  alSourcePlay(source);
+  SoundManager::check_al_error("Couldn't start audio source: ");
+}
+
+bool
+OpenALSoundSource::playing()
+{
+  ALint state = AL_PLAYING;
+  alGetSourcei(source, AL_SOURCE_STATE, &state);
+  return state != AL_STOPPED;
+}
+
+void
+OpenALSoundSource::update()
+{
+}
+
+void
+OpenALSoundSource::set_looping(bool looping)
+{
+  alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
+}
+
+void
+OpenALSoundSource::set_position(const Vector& position)
+{
+  alSource3f(source, AL_POSITION, position.x, position.y, 0);
+}
+
+void
+OpenALSoundSource::set_velocity(const Vector& velocity)
+{
+  alSource3f(source, AL_VELOCITY, velocity.x, velocity.y, 0);
+}
+
+void
+OpenALSoundSource::set_gain(float gain)
+{
+  alSourcef(source, AL_GAIN, gain);
+}
+
+void
+OpenALSoundSource::set_pitch(float pitch)
+{
+  alSourcef(source, AL_PITCH, pitch);
+}
+
+void
+OpenALSoundSource::set_reference_distance(float distance)
+{
+  alSourcef(source, AL_REFERENCE_DISTANCE, distance);
+}
+
+void
+OpenALSoundSource::set_rollof_factor(float factor)
+{
+  alSourcef(source, AL_ROLLOFF_FACTOR, factor);
+}
diff --git a/src/audio/openal_sound_source.hpp b/src/audio/openal_sound_source.hpp
new file mode 100644 (file)
index 0000000..4b03ed6
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __OPENAL_SOUND_SOURCE_H__
+#define __OPENAL_SOUND_SOURCE_H__
+
+#ifndef MACOSX
+#include <AL/al.h>
+#else
+#include <OpenAL/al.h>
+#endif
+
+#include "math/vector.hpp"
+#include "sound_source.hpp"
+
+class OpenALSoundSource : public SoundSource
+{
+public:
+  OpenALSoundSource();
+  virtual ~OpenALSoundSource();
+
+  virtual void play();
+  virtual void stop();
+  virtual bool playing();
+
+  virtual void update();
+
+  virtual void set_looping(bool looping);
+  virtual void set_gain(float gain);
+  virtual void set_pitch(float pitch);
+  virtual void set_position(const Vector& position);
+  virtual void set_velocity(const Vector& position);
+  virtual void set_reference_distance(float distance);
+  virtual void set_rollof_factor(float factor);
+
+protected:
+  friend class SoundManager;
+
+  ALuint source;
+};
+
+#endif
diff --git a/src/audio/sound_file.cpp b/src/audio/sound_file.cpp
new file mode 100644 (file)
index 0000000..2495bbb
--- /dev/null
@@ -0,0 +1,422 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** Used SDL_mixer and glest source as reference */
+#include <config.h>
+
+#include "sound_file.hpp"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <algorithm>
+#include <stdexcept>
+#include <sstream>
+#include <assert.h>
+#include <physfs.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "file_system.hpp"
+
+class WavSoundFile : public SoundFile
+{
+public:
+  WavSoundFile(PHYSFS_file* file);
+  ~WavSoundFile();
+
+  size_t read(void* buffer, size_t buffer_size);
+  void reset();
+
+private:
+  PHYSFS_file* file;
+
+  PHYSFS_sint64 datastart;
+};
+
+static inline uint32_t read32LE(PHYSFS_file* file)
+{
+  uint32_t result;
+  if(PHYSFS_readULE32(file, &result) == 0)
+    throw std::runtime_error("file too short");
+
+  return result;
+}
+
+static inline uint16_t read16LE(PHYSFS_file* file)
+{
+  uint16_t result;
+  if(PHYSFS_readULE16(file, &result) == 0)
+    throw std::runtime_error("file too short");
+
+  return result;
+}
+
+WavSoundFile::WavSoundFile(PHYSFS_file* file)
+{
+  this->file = file;
+
+  char magic[4];
+  if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+    throw std::runtime_error("Couldn't read file magic (not a wave file)");
+  if(strncmp(magic, "RIFF", 4) != 0) {
+    log_debug << "MAGIC: " << magic << std::endl;
+    throw std::runtime_error("file is not a RIFF wav file");
+  }
+
+  uint32_t wavelen = read32LE(file);
+  (void) wavelen;
+
+  if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+    throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
+  if(strncmp(magic, "WAVE", 4) != 0)
+    throw std::runtime_error("file is not a valid RIFF/WAVE file");
+
+  char chunkmagic[4];
+  uint32_t chunklen;
+
+  // search audio data format chunk
+  do {
+    if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
+      throw std::runtime_error("EOF while searching format chunk");
+    chunklen = read32LE(file);
+
+    if(strncmp(chunkmagic, "fmt ", 4) == 0)
+      break;
+
+    if(strncmp(chunkmagic, "fact", 4) == 0
+        || strncmp(chunkmagic, "LIST", 4) == 0) {
+      // skip chunk
+      if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
+        throw std::runtime_error("EOF while searching fmt chunk");
+    } else {
+      throw std::runtime_error("complex WAVE files not supported");
+    }
+  } while(true);
+
+  if(chunklen < 16)
+    throw std::runtime_error("Format chunk too short");
+
+  // parse format
+  uint16_t encoding = read16LE(file);
+  if(encoding != 1)
+    throw std::runtime_error("only PCM encoding supported");
+  channels = read16LE(file);
+  rate = read32LE(file);
+  uint32_t byterate = read32LE(file);
+  (void) byterate;
+  uint16_t blockalign = read16LE(file);
+  (void) blockalign;
+  bits_per_sample = read16LE(file);
+
+  if(chunklen > 16) {
+    if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
+      throw std::runtime_error("EOF while reading reast of format chunk");
+  }
+
+  // set file offset to DATA chunk data
+  do {
+    if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
+      throw std::runtime_error("EOF while searching data chunk");
+    chunklen = read32LE(file);
+
+    if(strncmp(chunkmagic, "data", 4) == 0)
+      break;
+
+    // skip chunk
+    if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
+      throw std::runtime_error("EOF while searching fmt chunk");
+  } while(true);
+
+  datastart = PHYSFS_tell(file);
+  size = static_cast<size_t> (chunklen);
+}
+
+WavSoundFile::~WavSoundFile()
+{
+  PHYSFS_close(file);
+}
+
+void
+WavSoundFile::reset()
+{
+  if(PHYSFS_seek(file, datastart) == 0)
+    throw std::runtime_error("Couldn't seek to data start");
+}
+
+size_t
+WavSoundFile::read(void* buffer, size_t buffer_size)
+{
+  PHYSFS_sint64 end = datastart + size;
+  PHYSFS_sint64 cur = PHYSFS_tell(file);
+  if(cur >= end)
+    return 0;
+
+  size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
+  if(PHYSFS_read(file, buffer, readsize, 1) != 1)
+    throw std::runtime_error("read error while reading samples");
+
+#ifdef WORDS_BIGENDIAN
+  if (bits_per_sample != 16)
+    return readsize;
+  char *tmp = (char*)buffer;
+
+  size_t i;
+  char c;
+  for (i = 0; i < readsize / 2; i++)
+  {
+    c          = tmp[2*i];
+    tmp[2*i]   = tmp[2*i+1];
+    tmp[2*i+1] = c;
+  }
+
+  buffer = tmp;
+#endif
+
+  return readsize;
+}
+
+//---------------------------------------------------------------------------
+
+class OggSoundFile : public SoundFile
+{
+public:
+  OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at);
+  ~OggSoundFile();
+
+  size_t read(void* buffer, size_t buffer_size);
+  void reset();
+
+private:
+  static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
+  static int cb_seek(void* source, ogg_int64_t offset, int whence);
+  static int cb_close(void* source);
+  static long cb_tell(void* source);
+
+  PHYSFS_file*   file;
+  OggVorbis_File vorbis_file;
+  ogg_int64_t    loop_begin;
+  ogg_int64_t    loop_at;
+  size_t         normal_buffer_loop;
+};
+
+OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at)
+{
+  this->file = file;
+
+  ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
+  ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
+
+  vorbis_info* vi = ov_info(&vorbis_file, -1);
+
+  channels        = vi->channels;
+  rate            = vi->rate;
+  bits_per_sample = 16;
+  size            = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
+
+  double sample_len    = 1.0f / rate;
+  double samples_begin = loop_begin / sample_len;
+  double sample_loop   = loop_at / sample_len;
+
+  this->loop_begin     = (ogg_int64_t) samples_begin;
+  if(loop_begin < 0) {
+    this->loop_at = (ogg_int64_t) -1;
+  } else {
+    this->loop_at = (ogg_int64_t) sample_loop;
+  }
+}
+
+OggSoundFile::~OggSoundFile()
+{
+  ov_clear(&vorbis_file);
+}
+
+size_t
+OggSoundFile::read(void* _buffer, size_t buffer_size)
+{
+  char*  buffer         = reinterpret_cast<char*> (_buffer);
+  int    section        = 0;
+  size_t totalBytesRead = 0;
+
+  while(buffer_size>0) {
+#ifdef WORDS_BIGENDIAN
+    int bigendian = 1;
+#else
+    int bigendian = 0;
+#endif
+
+    size_t bytes_to_read    = buffer_size;
+    if(loop_at > 0) {
+      size_t      bytes_per_sample       = 2;
+      ogg_int64_t time                   = ov_pcm_tell(&vorbis_file);
+      ogg_int64_t samples_left_till_loop = loop_at - time;
+      ogg_int64_t bytes_left_till_loop
+        = samples_left_till_loop * bytes_per_sample;
+      if(bytes_left_till_loop <= 4)
+        break;
+
+      if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) {
+        bytes_to_read    = (size_t) bytes_left_till_loop;
+      }
+    }
+
+    long bytesRead
+      = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian,
+          2, 1, &section);
+    if(bytesRead == 0) {
+      break;
+    }
+    buffer_size    -= bytesRead;
+    buffer         += bytesRead;
+    totalBytesRead += bytesRead;
+  }
+
+  return totalBytesRead;
+}
+
+void
+OggSoundFile::reset()
+{
+  ov_pcm_seek(&vorbis_file, loop_begin);
+}
+
+size_t
+OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
+{
+  PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+
+  PHYSFS_sint64 res
+    = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
+        static_cast<PHYSFS_uint32> (nmemb));
+  if(res <= 0)
+    return 0;
+
+  return static_cast<size_t> (res);
+}
+
+int
+OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
+{
+  PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+
+  switch(whence) {
+    case SEEK_SET:
+      if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
+        return -1;
+      break;
+    case SEEK_CUR:
+      if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
+        return -1;
+      break;
+    case SEEK_END:
+      if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
+        return -1;
+      break;
+    default:
+#ifdef DEBUG
+      assert(false);
+#else
+      return -1;
+#endif
+  }
+  return 0;
+}
+
+int
+OggSoundFile::cb_close(void* source)
+{
+  PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+  PHYSFS_close(file);
+  return 0;
+}
+
+long
+OggSoundFile::cb_tell(void* source)
+{
+  PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+  return static_cast<long> (PHYSFS_tell(file));
+}
+
+//---------------------------------------------------------------------------
+
+SoundFile* load_music_file(const std::string& filename)
+{
+  lisp::Parser parser(false);
+  const lisp::Lisp* root = parser.parse(filename);
+  const lisp::Lisp* music = root->get_lisp("supertux-music");
+  if(music == NULL)
+    throw std::runtime_error("file is not a supertux-music file.");
+
+  std::string raw_music_file;
+  float loop_begin = 0;
+  float loop_at    = -1;
+
+  music->get("file", raw_music_file);
+  music->get("loop-begin", loop_begin);
+  music->get("loop-at", loop_at);
+  
+  if(loop_begin < 0) {
+    throw std::runtime_error("can't loop from negative value");
+  }
+
+  std::string basedir = FileSystem::dirname(filename);
+  raw_music_file = FileSystem::normalize(basedir + raw_music_file);
+
+  PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str());
+  if(!file) {
+    std::stringstream msg;
+    msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError();
+    throw std::runtime_error(msg.str());
+  }
+
+  return new OggSoundFile(file, loop_begin, loop_at);
+}
+
+SoundFile* load_sound_file(const std::string& filename)
+{
+  if(filename.length() > 6
+      && filename.compare(filename.length()-6, 6, ".music") == 0) {
+    return load_music_file(filename);
+  }
+
+  PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
+  if(!file) {
+    std::stringstream msg;
+    msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
+    throw std::runtime_error(msg.str());
+  }
+
+  try {
+    char magic[4];
+    if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+      throw std::runtime_error("Couldn't read magic, file too short");
+    PHYSFS_seek(file, 0);
+    if(strncmp(magic, "RIFF", 4) == 0)
+      return new WavSoundFile(file);
+    else if(strncmp(magic, "OggS", 4) == 0)
+      return new OggSoundFile(file, 0, -1);
+    else
+      throw std::runtime_error("Unknown file format");
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Couldn't read '" << filename << "': " << e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
diff --git a/src/audio/sound_file.hpp b/src/audio/sound_file.hpp
new file mode 100644 (file)
index 0000000..fd777b2
--- /dev/null
@@ -0,0 +1,44 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SOUND_FILE_H__
+#define __SOUND_FILE_H__
+
+#include <stdio.h>
+#include <iostream>
+
+class SoundFile
+{
+public:
+  virtual ~SoundFile()
+  { }
+
+  virtual size_t read(void* buffer, size_t buffer_size) = 0;
+  virtual void reset() = 0;
+
+  int channels;
+  int rate;
+  int bits_per_sample;
+  /// size in bytes
+  size_t size;
+};
+
+SoundFile* load_sound_file(const std::string& filename);
+
+#endif
diff --git a/src/audio/sound_manager.cpp b/src/audio/sound_manager.cpp
new file mode 100644 (file)
index 0000000..7d285ff
--- /dev/null
@@ -0,0 +1,430 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "sound_manager.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <assert.h>
+#include <SDL.h>
+
+#include "sound_file.hpp"
+#include "sound_source.hpp"
+#include "openal_sound_source.hpp"
+#include "stream_sound_source.hpp"
+#include "dummy_sound_source.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+#ifndef DEBUG
+  /** Older openal versions often miss this function and it isn't that vital for
+   * supertux...
+   */
+#ifdef alcGetString
+#undef alcGetString
+#endif
+#define alcGetString(x,y) ""
+#endif
+
+SoundManager* sound_manager = 0;
+
+SoundManager::SoundManager()
+  : device(0), context(0), sound_enabled(false), music_source(0),
+    music_enabled(false)
+{
+  try {
+    device = alcOpenDevice(0);
+    if (device == NULL) {
+      throw std::runtime_error("Couldn't open audio device.");
+    }
+
+    int attributes[] = { 0 };
+    context = alcCreateContext(device, attributes);
+    check_alc_error("Couldn't create audio context: ");
+    alcMakeContextCurrent(context);
+    check_alc_error("Couldn't select audio context: ");
+
+    check_al_error("Audio error after init: ");
+    sound_enabled = true;
+    music_enabled = true;
+  } catch(std::exception& e) {
+    if(context != NULL)
+      alcDestroyContext(context);
+    context = NULL;
+    if(device != NULL)
+      alcCloseDevice(device);
+    device = NULL;
+    log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
+    print_openal_version();
+  }
+}
+
+SoundManager::~SoundManager()
+{
+  delete music_source;
+
+  for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
+    delete *i;
+  }
+
+  for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
+    ALuint buffer = i->second;
+    alDeleteBuffers(1, &buffer);
+  }
+
+  if(context != NULL) {
+    alcDestroyContext(context);
+  }
+  if(device != NULL) {
+    alcCloseDevice(device);
+  }
+}
+
+ALuint
+SoundManager::load_file_into_buffer(SoundFile* file)
+{
+  ALenum format = get_sample_format(file);
+  ALuint buffer;
+  alGenBuffers(1, &buffer);
+  check_al_error("Couldn't create audio buffer: ");
+  char* samples = new char[file->size];
+  try {
+    file->read(samples, file->size);
+    alBufferData(buffer, format, samples,
+        static_cast<ALsizei> (file->size),
+        static_cast<ALsizei> (file->rate));
+    check_al_error("Couldn't fill audio buffer: ");
+  } catch(...) {
+    delete[] samples;
+    throw;
+  }
+  delete[] samples;
+
+  return buffer;
+}
+
+OpenALSoundSource*
+SoundManager::intern_create_sound_source(const std::string& filename)
+{
+  if(!sound_enabled)
+    throw std::runtime_error("sound disabled");
+
+  std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
+
+  ALuint buffer;
+
+  // reuse an existing static sound buffer
+  SoundBuffers::iterator i = buffers.find(filename);
+  if(i != buffers.end()) {
+    buffer = i->second;
+  } else {
+    // Load sound file
+    std::auto_ptr<SoundFile> file (load_sound_file(filename));
+
+    if(file->size < 100000) {
+      buffer = load_file_into_buffer(file.get());
+      buffers.insert(std::make_pair(filename, buffer));
+    } else {
+      StreamSoundSource* source = new StreamSoundSource();
+      source->set_sound_file(file.release());
+      return source;
+    }
+  }
+
+  alSourcei(source->source, AL_BUFFER, buffer);
+  return source.release();
+}
+
+SoundSource*
+SoundManager::create_sound_source(const std::string& filename)
+{
+  if(!sound_enabled)
+    return create_dummy_sound_source();
+
+  try {
+    return intern_create_sound_source(filename);
+  } catch(std::exception &e) {
+    log_warning << "Couldn't create audio source: " << e.what() << std::endl;
+    return create_dummy_sound_source();
+  }
+}
+
+void
+SoundManager::preload(const std::string& filename)
+{
+  if(!sound_enabled)
+    return;
+
+  SoundBuffers::iterator i = buffers.find(filename);
+  // already loaded?
+  if(i != buffers.end())
+    return;
+
+  std::auto_ptr<SoundFile> file (load_sound_file(filename));
+  // only keep small files
+  if(file->size >= 100000)
+    return;
+
+  ALuint buffer = load_file_into_buffer(file.get());
+  buffers.insert(std::make_pair(filename, buffer));
+}
+
+void
+SoundManager::play(const std::string& filename, const Vector& pos)
+{
+  if(!sound_enabled)
+    return;
+
+  try {
+    std::auto_ptr<OpenALSoundSource> source
+        (intern_create_sound_source(filename));
+
+    if(pos == Vector(-1, -1)) {
+      source->set_rollof_factor(0);
+    } else {
+      source->set_position(pos);
+    }
+    source->play();
+    sources.push_back(source.release());
+  } catch(std::exception& e) {
+    log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
+  }
+}
+
+void
+SoundManager::manage_source(SoundSource* source)
+{
+  assert(source != NULL);
+
+  OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
+  if(openal_source != NULL) {
+    sources.push_back(openal_source);
+  }
+}
+
+void
+SoundManager::register_for_update( StreamSoundSource* sss ){
+  if( sss != NULL ){
+    update_list.push_back( sss );
+  }
+}
+
+void
+SoundManager::remove_from_update( StreamSoundSource* sss  ){
+  if( sss != NULL ){
+    StreamSoundSources::iterator i = update_list.begin();
+       while( i != update_list.end() ){
+      if( *i == sss ){
+        i = update_list.erase(i);
+      } else {
+        i++;
+      }
+    }
+  }
+}
+
+void
+SoundManager::enable_sound(bool enable)
+{
+  if(device == NULL)
+    return;
+
+  sound_enabled = enable;
+}
+
+void
+SoundManager::enable_music(bool enable)
+{
+  if(device == NULL)
+    return;
+
+  music_enabled = enable;
+  if(music_enabled) {
+    play_music(current_music);
+  } else {
+    if(music_source) {
+      delete music_source;
+      music_source = 0;
+    }
+  }
+}
+
+void
+SoundManager::stop_music(float fadetime)
+{
+  if(fadetime > 0) {
+    if(music_source
+        && music_source->get_fade_state() != StreamSoundSource::FadingOff)
+      music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
+  } else {
+    delete music_source;
+    music_source = NULL;
+  }
+  current_music = "";
+}
+
+void
+SoundManager::play_music(const std::string& filename, bool fade)
+{
+  if(filename == current_music && music_source != NULL)
+    return;
+  current_music = filename;
+  if(!music_enabled)
+    return;
+
+  if(filename == "") {
+    delete music_source;
+    music_source = NULL;
+    return;
+  }
+
+  try {
+    std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
+    alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
+    newmusic->set_sound_file(load_sound_file(filename));
+    newmusic->set_looping(true);
+    if(fade)
+      newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
+    newmusic->play();
+
+    delete music_source;
+    music_source = newmusic.release();
+  } catch(std::exception& e) {
+    log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
+  }
+}
+
+void
+SoundManager::set_listener_position(const Vector& pos)
+{
+  static Uint32 lastticks = SDL_GetTicks();
+
+  Uint32 current_ticks = SDL_GetTicks();
+  if(current_ticks - lastticks < 300)
+    return;
+  lastticks = current_ticks;
+
+  alListener3f(AL_POSITION, pos.x, pos.y, 0);
+}
+
+void
+SoundManager::set_listener_velocity(const Vector& vel)
+{
+  alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
+}
+
+void
+SoundManager::update()
+{
+  static Uint32 lasttime = SDL_GetTicks();
+  Uint32 now = SDL_GetTicks();
+
+  if(now - lasttime < 300)
+    return;
+  lasttime = now;
+
+  // update and check for finished sound sources
+  for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
+    OpenALSoundSource* source = *i;
+
+    source->update();
+
+    if(!source->playing()) {
+      delete source;
+      i = sources.erase(i);
+    } else {
+      ++i;
+    }
+  }
+  // check streaming sounds
+  if(music_source) {
+    music_source->update();
+  }
+
+  if (context)
+  {
+    alcProcessContext(context);
+    check_alc_error("Error while processing audio context: ");
+  }
+
+  //run update() for stream_sound_source
+  StreamSoundSources::iterator s = update_list.begin();
+  while( s != update_list.end() ){
+    (*s)->update();
+    s++;
+  }
+}
+
+ALenum
+SoundManager::get_sample_format(SoundFile* file)
+{
+  if(file->channels == 2) {
+    if(file->bits_per_sample == 16) {
+      return AL_FORMAT_STEREO16;
+    } else if(file->bits_per_sample == 8) {
+      return AL_FORMAT_STEREO8;
+    } else {
+      throw std::runtime_error("Only 16 and 8 bit samples supported");
+    }
+  } else if(file->channels == 1) {
+    if(file->bits_per_sample == 16) {
+      return AL_FORMAT_MONO16;
+    } else if(file->bits_per_sample == 8) {
+      return AL_FORMAT_MONO8;
+    } else {
+      throw std::runtime_error("Only 16 and 8 bit samples supported");
+    }
+  }
+
+  throw std::runtime_error("Only 1 and 2 channel samples supported");
+}
+
+void
+SoundManager::print_openal_version()
+{
+  log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
+  log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
+  log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
+  log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
+}
+
+void
+SoundManager::check_alc_error(const char* message)
+{
+  int err = alcGetError(device);
+  if(err != ALC_NO_ERROR) {
+    std::stringstream msg;
+    msg << message << alcGetString(device, err);
+    throw std::runtime_error(msg.str());
+  }
+}
+
+void
+SoundManager::check_al_error(const char* message)
+{
+  int err = alGetError();
+  if(err != AL_NO_ERROR) {
+    std::stringstream msg;
+    msg << message << alGetString(err);
+    throw std::runtime_error(msg.str());
+  }
+}
diff --git a/src/audio/sound_manager.hpp b/src/audio/sound_manager.hpp
new file mode 100644 (file)
index 0000000..12f1944
--- /dev/null
@@ -0,0 +1,125 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SOUND_MANAGER_H__
+#define __SOUND_MANAGER_H__
+
+#include <string>
+#include <vector>
+#include <map>
+
+#ifndef MACOSX
+#include <AL/alc.h>
+#include <AL/al.h>
+#else
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#endif
+
+#include "math/vector.hpp"
+
+class SoundFile;
+class SoundSource;
+class StreamSoundSource;
+class OpenALSoundSource;
+
+class SoundManager
+{
+public:
+  SoundManager();
+  virtual ~SoundManager();
+
+  void enable_sound(bool sound_enabled);
+  /**
+   * Creates a new sound source object which plays the specified soundfile.
+   * You are responsible for deleting the sound source later (this will stop the
+   * sound).
+   * This function never throws exceptions, but might return a DummySoundSource
+   */
+  SoundSource* create_sound_source(const std::string& filename);
+  /**
+   * Convenience function to simply play a sound at a given position.
+   */
+  void play(const std::string& name, const Vector& pos = Vector(-1, -1));
+  /**
+   * Adds the source to the list of managed sources (= the source gets deleted
+   * when it finished playing)
+   */
+  void manage_source(SoundSource* source);
+  /// preloads a sound, so that you don't get a lag later when playing it
+  void preload(const std::string& name);
+
+  void set_listener_position(const Vector& position);
+  void set_listener_velocity(const Vector& velocity);
+
+  void enable_music(bool music_enabled);
+  void play_music(const std::string& filename, bool fade = false);
+  void stop_music(float fadetime = 0);
+
+  bool is_music_enabled() { return music_enabled; }
+  bool is_sound_enabled() { return sound_enabled; }
+
+  bool is_audio_enabled() {
+                       return device != 0 && context != 0;
+  }
+
+  void update();
+
+  /*
+   * Tell soundmanager to call update() for stream_sound_source.
+   */
+  void register_for_update( StreamSoundSource* sss );
+  /*
+   * Unsubscribe from updates for stream_sound_source.
+   */
+  void remove_from_update( StreamSoundSource* sss );
+
+private:
+  friend class OpenALSoundSource;
+  friend class StreamSoundSource;
+
+  /** creates a new sound source, might throw exceptions, never returns NULL */
+  OpenALSoundSource* intern_create_sound_source(const std::string& filename);
+  static ALuint load_file_into_buffer(SoundFile* file);
+  static ALenum get_sample_format(SoundFile* file);
+
+  void print_openal_version();
+  void check_alc_error(const char* message);
+  static void check_al_error(const char* message);
+
+  ALCdevice* device;
+  ALCcontext* context;
+  bool sound_enabled;
+
+  typedef std::map<std::string, ALuint> SoundBuffers;
+  SoundBuffers buffers;
+  typedef std::vector<OpenALSoundSource*> SoundSources;
+  SoundSources sources;
+
+  typedef std::vector<StreamSoundSource*> StreamSoundSources;
+  StreamSoundSources update_list;
+
+  StreamSoundSource* music_source;
+
+  bool music_enabled;
+  std::string current_music;
+};
+
+extern SoundManager* sound_manager;
+
+#endif
diff --git a/src/audio/sound_source.hpp b/src/audio/sound_source.hpp
new file mode 100644 (file)
index 0000000..48c3f28
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SOUND_SOURCE_H__
+#define __SOUND_SOURCE_H__
+
+#include "math/vector.hpp"
+
+/**
+ * A sound source represents the source of audio output. You can place
+ * sources at certain points in your world or set their velocity to produce
+ * doppler effects
+ */
+class SoundSource
+{
+public:
+  virtual ~SoundSource()
+  { }
+
+  virtual void play() = 0;
+  virtual void stop() = 0;
+  virtual bool playing() = 0;
+
+  virtual void set_looping(bool looping) = 0;
+  /// Set volume (0.0 is silent, 1.0 is normal)
+  virtual void set_gain(float gain) = 0;
+  virtual void set_pitch(float pitch) = 0;
+  virtual void set_position(const Vector& position) = 0;
+  virtual void set_velocity(const Vector& position) = 0;
+  virtual void set_reference_distance(float distance) = 0;
+  virtual void set_rollof_factor(float factor) = 0;
+};
+
+#endif
diff --git a/src/audio/stream_sound_source.cpp b/src/audio/stream_sound_source.cpp
new file mode 100644 (file)
index 0000000..5a01723
--- /dev/null
@@ -0,0 +1,144 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <assert.h>
+
+#include <SDL.h>
+
+#include "stream_sound_source.hpp"
+#include "sound_manager.hpp"
+#include "sound_file.hpp"
+#include "timer.hpp"
+#include "log.hpp"
+
+StreamSoundSource::StreamSoundSource()
+  : file(0), fade_state(NoFading), looping(false)
+{
+  alGenBuffers(STREAMFRAGMENTS, buffers);
+  SoundManager::check_al_error("Couldn't allocate audio buffers: ");
+  //add me to update list
+  sound_manager->register_for_update( this );
+}
+
+StreamSoundSource::~StreamSoundSource()
+{
+  //don't update me any longer
+  sound_manager->remove_from_update( this );
+  delete file;
+  stop();
+  alDeleteBuffers(STREAMFRAGMENTS, buffers);
+  SoundManager::check_al_error("Couldn't delete audio buffers: ");
+}
+
+void
+StreamSoundSource::set_sound_file(SoundFile* newfile)
+{
+  delete file;
+  file = newfile;
+
+  ALint queued;
+  alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
+  for(size_t i = 0; i < STREAMFRAGMENTS - queued; ++i) {
+    if(fillBufferAndQueue(buffers[i]) == false)
+      break;
+  }
+}
+
+void
+StreamSoundSource::update()
+{
+  ALint processed = 0;
+  alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
+  for(ALint i = 0; i < processed; ++i) {
+    ALuint buffer;
+    alSourceUnqueueBuffers(source, 1, &buffer);
+    SoundManager::check_al_error("Couldn't unqueu audio buffer: ");
+
+    if(fillBufferAndQueue(buffer) == false)
+      break;
+  }
+
+  if(!playing()) {
+    if(processed == 0 || !looping)
+      return;
+
+    // we might have to restart the source if we had a buffer underrun
+    log_info << "Restarting audio source because of buffer underrun" << std::endl;
+    play();
+  }
+
+  if(fade_state == FadingOn) {
+    float time = real_time - fade_start_time;
+    if(time >= fade_time) {
+      set_gain(1.0);
+      fade_state = NoFading;
+    } else {
+      set_gain(time / fade_time);
+    }
+  } else if(fade_state == FadingOff) {
+    float time = real_time - fade_start_time;
+    if(time >= fade_time) {
+      stop();
+      fade_state = NoFading;
+    } else {
+      set_gain( (fade_time-time) / fade_time);
+    }
+  }
+}
+
+void
+StreamSoundSource::set_fading(FadeState state, float fade_time)
+{
+  this->fade_state = state;
+  this->fade_time = fade_time;
+  this->fade_start_time = real_time;
+}
+
+bool
+StreamSoundSource::fillBufferAndQueue(ALuint buffer)
+{
+  // fill buffer
+  char* bufferdata = new char[STREAMFRAGMENTSIZE];
+  size_t bytesread = 0;
+  do {
+    bytesread += file->read(bufferdata + bytesread,
+        STREAMFRAGMENTSIZE - bytesread);
+    // end of sound file
+    if(bytesread < STREAMFRAGMENTSIZE) {
+      if(looping)
+        file->reset();
+      else
+        break;
+    }
+  } while(bytesread < STREAMFRAGMENTSIZE);
+
+  if(bytesread > 0) {
+    ALenum format = SoundManager::get_sample_format(file);
+    alBufferData(buffer, format, bufferdata, bytesread, file->rate);
+    SoundManager::check_al_error("Couldn't refill audio buffer: ");
+
+    alSourceQueueBuffers(source, 1, &buffer);
+    SoundManager::check_al_error("Couldn't queue audio buffer: ");
+  }
+  delete[] bufferdata;
+
+  // return false if there aren't more buffers to fill
+  return bytesread >= STREAMFRAGMENTSIZE;
+}
diff --git a/src/audio/stream_sound_source.hpp b/src/audio/stream_sound_source.hpp
new file mode 100644 (file)
index 0000000..aa621b3
--- /dev/null
@@ -0,0 +1,71 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __STREAM_SOUND_SOURCE_H__
+#define __STREAM_SOUND_SOURCE_H__
+
+#include <stdio.h>
+#include <SDL.h>
+#include "openal_sound_source.hpp"
+
+class SoundFile;
+
+class StreamSoundSource : public OpenALSoundSource
+{
+public:
+  StreamSoundSource();
+  virtual ~StreamSoundSource();
+
+  void set_sound_file(SoundFile* file);
+
+  enum FadeState { NoFading, FadingOn, FadingOff };
+
+  void set_fading(FadeState state, float fadetime);
+  FadeState get_fade_state() const
+  {
+    return fade_state;
+  }
+  void update();
+
+  void set_looping(bool looping)
+  {
+    this->looping = looping;
+  }
+  bool get_looping() const
+  {
+    return looping;
+  }
+
+private:
+  static const size_t STREAMBUFFERSIZE = 1024 * 500;
+  static const size_t STREAMFRAGMENTS = 5;
+  static const size_t STREAMFRAGMENTSIZE
+    = STREAMBUFFERSIZE / STREAMFRAGMENTS;
+
+  bool fillBufferAndQueue(ALuint buffer);
+  SoundFile* file;
+  ALuint buffers[STREAMFRAGMENTS];
+
+  FadeState fade_state;
+  float fade_start_time;
+  float fade_time;
+  bool looping;
+};
+
+#endif
diff --git a/src/badguy/angrystone.cpp b/src/badguy/angrystone.cpp
new file mode 100644 (file)
index 0000000..865e430
--- /dev/null
@@ -0,0 +1,177 @@
+//  $Id$
+//
+//  AngryStone - A spiked block that charges towards the player
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+
+#include "angrystone.hpp"
+
+static const float SPEED = 240;
+
+static const float CHARGE_TIME = .5;
+static const float ATTACK_TIME = 1;
+static const float RECOVER_TIME = .5;
+
+AngryStone::AngryStone(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/angrystone/angrystone.sprite"), state(IDLE)
+{
+}
+
+void
+AngryStone::write(lisp::Writer& writer)
+{
+  writer.start_list("angrystone");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("angrystone");
+}
+
+void
+AngryStone::activate()
+{
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+  physic.enable_gravity(true);
+  sprite->set_action("idle");
+}
+
+void
+AngryStone::collision_solid(const CollisionHit& hit)
+{
+  // TODO
+  (void) hit;
+#if 0
+  if ((state == ATTACKING) &&
+      (hit.normal.x == -attackDirection.x) && (hit.normal.y == attackDirection.y)) {
+    state = IDLE;
+    sprite->set_action("idle");
+    physic.set_velocity_x(0);
+    physic.set_velocity_y(0);
+    physic.enable_gravity(true);
+    oldWallDirection.x = attackDirection.x;
+    oldWallDirection.y = attackDirection.y;
+  }
+#endif
+}
+
+void
+AngryStone::kill_fall()
+{
+  //prevents AngryStone from getting killed by other enemies or the player
+}
+
+HitResponse
+AngryStone::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+  if (state == ATTACKING) {
+    badguy.kill_fall();
+    return FORCE_MOVE;
+  }
+
+  return FORCE_MOVE;
+}
+
+void
+AngryStone::active_update(float elapsed_time) {
+  BadGuy::active_update(elapsed_time);
+
+  if (state == IDLE) {
+    MovingObject* player = this->get_nearest_player();
+    MovingObject* badguy = this;
+    const Vector playerPos = player->get_pos();
+    const Vector badguyPos = badguy->get_pos();
+    float dx = (playerPos.x - badguyPos.x);
+    float dy = (playerPos.y - badguyPos.y);
+
+    float playerHeight = player->get_bbox().p2.y - player->get_bbox().p1.y;
+    float badguyHeight = badguy->get_bbox().p2.y - badguy->get_bbox().p1.y;
+
+    float playerWidth = player->get_bbox().p2.x - player->get_bbox().p1.x;
+    float badguyWidth = badguy->get_bbox().p2.x - badguy->get_bbox().p1.x;
+
+    if ((dx > -playerWidth) && (dx < badguyWidth)) {
+      if (dy > 0) {
+        attackDirection.x = 0;
+        attackDirection.y = 1;
+      } else {
+        attackDirection.x = 0;
+        attackDirection.y = -1;
+      }
+      if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
+        sprite->set_action("charging");
+        timer.start(CHARGE_TIME);
+        state = CHARGING;
+      }
+    } else
+    if ((dy > -playerHeight) && (dy < badguyHeight)) {
+      if (dx > 0) {
+        attackDirection.x = 1;
+        attackDirection.y = 0;
+      } else {
+        attackDirection.x = -1;
+        attackDirection.y = 0;
+      }
+      if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
+        sprite->set_action("charging");
+        timer.start(CHARGE_TIME);
+        state = CHARGING;
+      }
+    }
+
+  }
+
+  if (state == CHARGING) {
+    if (timer.check()) {
+      sprite->set_action("attacking");
+      timer.start(ATTACK_TIME);
+      state = ATTACKING;
+      physic.enable_gravity(false);
+      physic.set_velocity_x(SPEED * attackDirection.x);
+      physic.set_velocity_y(SPEED * attackDirection.y);
+      oldWallDirection.x = 0;
+      oldWallDirection.y = 0;
+    }
+  }
+
+  if (state == ATTACKING) {
+    if (timer.check()) {
+      timer.start(RECOVER_TIME);
+      state = RECOVERING;
+      sprite->set_action("idle");
+      physic.enable_gravity(true);
+      physic.set_velocity_x(0);
+      physic.set_velocity_y(0);
+    }
+  }
+
+  if (state == RECOVERING) {
+    if (timer.check()) {
+      state = IDLE;
+      sprite->set_action("idle");
+      physic.enable_gravity(true);
+      physic.set_velocity_x(0);
+      physic.set_velocity_y(0);
+    }
+  }
+
+}
+
+IMPLEMENT_FACTORY(AngryStone, "angrystone")
diff --git a/src/badguy/angrystone.hpp b/src/badguy/angrystone.hpp
new file mode 100644 (file)
index 0000000..7abd8f1
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  AngryStone - A spiked block that charges towards the player
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __ANGRYSTONE_H__
+#define __ANGRYSTONE_H__
+
+#include "badguy.hpp"
+
+class AngryStone : public BadGuy
+{
+public:
+  AngryStone(const lisp::Lisp& reader);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  void active_update(float elapsed_time);
+  void kill_fall();
+
+  virtual AngryStone* clone() const { return new AngryStone(*this); }
+
+protected:
+  Vector attackDirection;  /**< 1-normalized vector of current attack direction */
+  Vector oldWallDirection; /**< if wall was hit during last attack: 1-normalized vector of last attack direction, (0,0) otherwise */
+
+  Timer timer;
+
+  enum AngryStoneState {
+    IDLE,
+    CHARGING,
+    ATTACKING,
+    RECOVERING
+  };
+  AngryStoneState state;
+
+};
+
+#endif
diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp
new file mode 100644 (file)
index 0000000..19e8d8b
--- /dev/null
@@ -0,0 +1,580 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "badguy.hpp"
+#include "object/camera.hpp"
+#include "object/tilemap.hpp"
+#include "tile.hpp"
+#include "statistics.hpp"
+#include "game_session.hpp"
+#include "log.hpp"
+#include "level.hpp"
+#include "object/bullet.hpp"
+#include "main.hpp"
+#include "object/particles.hpp"
+#include "random_generator.hpp"
+
+static const float SQUISH_TIME = 2;
+static const float X_OFFSCREEN_DISTANCE = 1600;
+static const float Y_OFFSCREEN_DISTANCE = 1200;
+
+BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name, int layer)
+  : MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), countMe(true),
+    dir(LEFT), start_dir(AUTO), frozen(false), ignited(false),
+    state(STATE_INIT), on_ground_flag(false)
+{
+  start_position = bbox.p1;
+
+  sound_manager->preload("sounds/squish.wav");
+  sound_manager->preload("sounds/fall.wav");
+}
+
+BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer)
+  : MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), countMe(true),
+    dir(direction), start_dir(direction), frozen(false), ignited(false),
+    state(STATE_INIT), on_ground_flag(false)
+{
+  start_position = bbox.p1;
+
+  sound_manager->preload("sounds/squish.wav");
+  sound_manager->preload("sounds/fall.wav");
+}
+
+BadGuy::BadGuy(const lisp::Lisp& reader, const std::string& sprite_name, int layer)
+  : MovingSprite(reader, sprite_name, layer, COLGROUP_DISABLED), countMe(true), dir(LEFT), start_dir(AUTO), frozen(false), ignited(false), state(STATE_INIT), on_ground_flag(false)
+{
+  start_position = bbox.p1;
+
+  std::string dir_str = "auto";
+  reader.get("direction", dir_str);
+  start_dir = str2dir( dir_str );
+  dir = start_dir;
+
+  reader.get("dead-script", dead_script);
+
+  sound_manager->preload("sounds/squish.wav");
+  sound_manager->preload("sounds/fall.wav");
+}
+
+void
+BadGuy::draw(DrawingContext& context)
+{
+  if(!sprite)
+    return;
+  if(state == STATE_INIT || state == STATE_INACTIVE)
+    return;
+  if(state == STATE_FALLING) {
+    DrawingEffect old_effect = context.get_drawing_effect();
+    context.set_drawing_effect((DrawingEffect) (old_effect | VERTICAL_FLIP));
+    sprite->draw(context, get_pos(), layer);
+    context.set_drawing_effect(old_effect);
+  } else {
+    sprite->draw(context, get_pos(), layer);
+  }
+}
+
+void
+BadGuy::update(float elapsed_time)
+{
+  if(!Sector::current()->inside(bbox)) {
+    remove_me();
+    return;
+  }
+  if(is_offscreen()) {
+    if (state == STATE_ACTIVE) deactivate();
+    set_state(STATE_INACTIVE);
+  }
+
+  switch(state) {
+    case STATE_ACTIVE:
+      active_update(elapsed_time);
+      break;
+    case STATE_INIT:
+    case STATE_INACTIVE:
+      inactive_update(elapsed_time);
+      try_activate();
+      break;
+    case STATE_SQUISHED:
+      if(state_timer.check()) {
+        remove_me();
+        break;
+      }
+      movement = physic.get_movement(elapsed_time);
+      break;
+    case STATE_FALLING:
+      movement = physic.get_movement(elapsed_time);
+      break;
+  }
+
+  on_ground_flag = false;
+}
+
+Direction
+BadGuy::str2dir( std::string dir_str )
+{
+  if( dir_str == "auto" )
+    return AUTO;
+  if( dir_str == "left" )
+    return LEFT;
+  if( dir_str == "right" )
+    return RIGHT;
+
+  //default to "auto"
+  log_warning << "Badguy::str2dir: unknown direction \"" << dir_str << "\"" << std::endl;;
+  return AUTO;
+}
+
+void
+BadGuy::activate()
+{
+}
+
+void
+BadGuy::deactivate()
+{
+}
+
+void
+BadGuy::write(lisp::Writer& )
+{
+  log_warning << "tried to write out a generic badguy" << std::endl;
+}
+
+void
+BadGuy::active_update(float elapsed_time)
+{
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+BadGuy::inactive_update(float )
+{
+}
+
+void
+BadGuy::collision_tile(uint32_t tile_attributes)
+{
+  if(tile_attributes & Tile::HURTS) {
+    if (tile_attributes & Tile::FIRE) {
+      if (is_flammable()) ignite();
+    }
+    else if (tile_attributes & Tile::ICE) {
+      if (is_freezable()) freeze();
+    }
+    else {
+      kill_fall();
+    }
+  }
+}
+
+HitResponse
+BadGuy::collision(GameObject& other, const CollisionHit& hit)
+{
+  switch(state) {
+    case STATE_INIT:
+    case STATE_INACTIVE:
+      return ABORT_MOVE;
+    case STATE_ACTIVE: {
+      BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+      if(badguy && badguy->state == STATE_ACTIVE && badguy->get_group() == COLGROUP_MOVING) {
+
+       // hit from above?
+       if (badguy->get_bbox().p2.y < (bbox.p1.y + 16)) {
+         if(collision_squished(*badguy)) {
+           return ABORT_MOVE;
+         }
+       }
+
+       return collision_badguy(*badguy, hit);
+      }
+
+      Player* player = dynamic_cast<Player*> (&other);
+      if(player) {
+
+       // hit from above?
+       if (player->get_bbox().p2.y < (bbox.p1.y + 16)) {
+         if(collision_squished(*player)) {
+           return ABORT_MOVE;
+         }
+       }
+
+        return collision_player(*player, hit);
+      }
+
+      Bullet* bullet = dynamic_cast<Bullet*> (&other);
+      if(bullet)
+        return collision_bullet(*bullet, hit);
+
+      return FORCE_MOVE;
+    }
+    case STATE_SQUISHED:
+      return FORCE_MOVE;
+    case STATE_FALLING:
+      return FORCE_MOVE;
+  }
+
+  return ABORT_MOVE;
+}
+
+void
+BadGuy::collision_solid(const CollisionHit& hit)
+{
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+  update_on_ground_flag(hit);
+}
+
+HitResponse
+BadGuy::collision_player(Player& player, const CollisionHit& )
+{
+  if(player.is_invincible()) {
+    kill_fall();
+    return ABORT_MOVE;
+  }
+
+  if(frozen)
+    unfreeze();
+  player.kill(false);
+  return FORCE_MOVE;
+}
+
+HitResponse
+BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
+
+bool
+BadGuy::collision_squished(GameObject& )
+{
+  return false;
+}
+
+HitResponse
+BadGuy::collision_bullet(Bullet& bullet, const CollisionHit& hit)
+{
+  if (is_frozen()) {
+    if(bullet.get_type() == FIRE_BONUS) {
+      // fire bullet thaws frozen badguys
+      unfreeze();
+      bullet.remove_me();
+      return ABORT_MOVE;
+    } else {
+      // other bullets ricochet
+      bullet.ricochet(*this, hit);
+      return FORCE_MOVE;
+    }
+  }
+  else if (is_ignited()) {
+    if(bullet.get_type() == ICE_BONUS) {
+      // ice bullets extinguish ignited badguys
+      extinguish();
+      bullet.remove_me();
+      return ABORT_MOVE;
+    } else {
+      // other bullets are absorbed by ignited badguys
+      bullet.remove_me();
+      return FORCE_MOVE;
+    }
+  }
+  else if(bullet.get_type() == FIRE_BONUS && is_flammable()) {
+    // fire bullets ignite flammable badguys
+    ignite();
+    bullet.remove_me();
+    return ABORT_MOVE;
+  }
+  else if(bullet.get_type() == ICE_BONUS && is_freezable()) {
+    // ice bullets freeze freezable badguys
+    freeze();
+    bullet.remove_me();
+    return ABORT_MOVE;
+  }
+  else {
+    // in all other cases, bullets ricochet
+    bullet.ricochet(*this, hit);
+    return FORCE_MOVE;
+  }
+}
+
+void
+BadGuy::kill_squished(GameObject& object)
+{
+  sound_manager->play("sounds/squish.wav", get_pos());
+  physic.enable_gravity(true);
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+  set_state(STATE_SQUISHED);
+  set_group(COLGROUP_MOVING_ONLY_STATIC);
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) {
+    if (countMe) Sector::current()->get_level()->stats.badguys++;
+    player->bounce(*this);
+  }
+
+  // start dead-script
+  if(dead_script != "") {
+    std::istringstream stream(dead_script);
+    Sector::current()->run_script(stream, "dead-script");
+  }
+}
+
+void
+BadGuy::kill_fall()
+{
+  sound_manager->play("sounds/fall.wav", get_pos());
+  if (countMe) Sector::current()->get_level()->stats.badguys++;
+  physic.set_velocity_y(0);
+  physic.enable_gravity(true);
+  set_state(STATE_FALLING);
+
+  // start dead-script
+  if(dead_script != "") {
+    std::istringstream stream(dead_script);
+    Sector::current()->run_script(stream, "dead-script");
+  }
+}
+
+void
+BadGuy::run_dead_script()
+{
+   if (countMe)
+     Sector::current()->get_level()->stats.badguys++;
+   
+   // start dead-script
+  if(dead_script != "") {
+    std::istringstream stream(dead_script);
+    Sector::current()->run_script(stream, "dead-script");
+  }
+}
+
+void
+BadGuy::set_state(State state)
+{
+  if(this->state == state)
+    return;
+
+  State laststate = this->state;
+  this->state = state;
+  switch(state) {
+    case STATE_SQUISHED:
+      state_timer.start(SQUISH_TIME);
+      break;
+    case STATE_ACTIVE:
+      set_group(COLGROUP_MOVING);
+      bbox.set_pos(start_position);
+      break;
+    case STATE_INACTIVE:
+      // was the badguy dead anyway?
+      if(laststate == STATE_SQUISHED || laststate == STATE_FALLING) {
+        remove_me();
+      }
+      set_group(COLGROUP_DISABLED);
+      break;
+    case STATE_FALLING:
+      set_group(COLGROUP_DISABLED);
+      break;
+    default:
+      break;
+  }
+}
+
+bool
+BadGuy::is_offscreen()
+{
+  float scroll_x = Sector::current()->camera->get_translation().x;
+  float scroll_y = Sector::current()->camera->get_translation().y;
+
+  if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
+      || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE + SCREEN_WIDTH
+      || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
+      || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE + SCREEN_HEIGHT)
+    return true;
+
+  return false;
+}
+
+void
+BadGuy::try_activate()
+{
+  float scroll_x = Sector::current()->camera->get_translation().x;
+  float scroll_y = Sector::current()->camera->get_translation().y;
+
+  /* Activate badguys if they're just around the screen to avoid
+   * the effect of having badguys suddenly popping up from nowhere.
+   */
+  //Badguy left of screen
+  if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+      start_position.x < scroll_x - bbox.get_width() &&
+      start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+      start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) {
+    if (start_dir != AUTO) dir = start_dir; else dir = RIGHT;
+    set_state(STATE_ACTIVE);
+    activate();
+  //Badguy right of screen
+  } else if (start_position.x > scroll_x +  SCREEN_WIDTH &&
+      start_position.x < scroll_x + SCREEN_WIDTH + X_OFFSCREEN_DISTANCE &&
+      start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+      start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) {
+    if (start_dir != AUTO) dir = start_dir; else dir = LEFT;
+    set_state(STATE_ACTIVE);
+    activate();
+  //Badguy over or under screen
+  } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+       start_position.x < scroll_x + SCREEN_WIDTH + X_OFFSCREEN_DISTANCE &&
+       ((start_position.y > scroll_y + SCREEN_HEIGHT &&
+         start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) ||
+        (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+         start_position.y < scroll_y - bbox.get_height()  ))) {
+     if (start_dir != AUTO) dir = start_dir;
+     else{
+         // if nearest player is to our right, start facing right
+         Player* player = get_nearest_player();
+         if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
+             dir = RIGHT;
+         } else {
+                dir = LEFT;
+         }
+     }
+     set_state(STATE_ACTIVE);
+     activate();
+  } else if(state == STATE_INIT
+      && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
+      && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE + SCREEN_WIDTH
+      && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
+      && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE + SCREEN_HEIGHT ) {
+    if (start_dir != AUTO) {
+      dir = start_dir;
+    } else {
+      // if nearest player is to our right, start facing right
+      Player* player = get_nearest_player();
+      if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
+       dir = RIGHT;
+      } else {
+       dir = LEFT;
+      }
+    }
+    set_state(STATE_ACTIVE);
+    activate();
+  }
+}
+
+bool
+BadGuy::might_fall(int height)
+{
+  // make sure we check for at least a 1-pixel fall
+  assert(height > 0);
+
+  float x1;
+  float x2;
+  float y1 = bbox.p2.y + 1;
+  float y2 = bbox.p2.y + 1 + height;
+  if (dir == LEFT) {
+    x1 = bbox.p1.x - 1;
+    x2 = bbox.p1.x - 1;
+  } else {
+    x1 = bbox.p2.x + 1;
+    x2 = bbox.p2.x + 1;
+  }
+  return Sector::current()->is_free_of_statics(Rect(x1, y1, x2, y2));
+}
+
+Player*
+BadGuy::get_nearest_player()
+{
+  // FIXME: does not really return nearest player
+
+  std::vector<Player*> players = Sector::current()->get_players();
+  for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+    Player* player = *playerIter;
+    return player;
+  }
+
+  return 0;
+}
+
+void
+BadGuy::update_on_ground_flag(const CollisionHit& hit)
+{
+  if (hit.bottom) {
+    on_ground_flag = true;
+    floor_normal = hit.slope_normal;
+  }
+}
+
+bool
+BadGuy::on_ground()
+{
+  return on_ground_flag;
+}
+
+Vector
+BadGuy::get_floor_normal()
+{
+  return floor_normal;
+}
+
+void
+BadGuy::freeze()
+{
+  set_group(COLGROUP_MOVING_STATIC);
+  frozen = true;
+}
+
+void
+BadGuy::unfreeze()
+{
+  set_group(COLGROUP_MOVING);
+  frozen = false;
+}
+
+bool
+BadGuy::is_freezable() const
+{
+  return false;
+}
+
+bool
+BadGuy::is_frozen() const
+{
+  return frozen;
+}
+
+void
+BadGuy::ignite()
+{
+  kill_fall();
+}
+
+void
+BadGuy::extinguish()
+{
+}
+
+bool
+BadGuy::is_flammable() const
+{
+  return true;
+}
+
+bool
+BadGuy::is_ignited() const
+{
+  return ignited;
+}
diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp
new file mode 100644 (file)
index 0000000..6c18166
--- /dev/null
@@ -0,0 +1,243 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BADGUY_H__
+#define __BADGUY_H__
+
+// moved them here to make it less typing when implementing new badguys
+#include <math.h>
+#include "timer.hpp"
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "object/player.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "direction.hpp"
+#include "object_factory.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "audio/sound_source.hpp"
+
+class BadGuy : public MovingSprite, protected UsesPhysic, public Serializable
+{
+public:
+  BadGuy(const Vector& pos, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+  BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+  BadGuy(const lisp::Lisp& reader, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+
+  /** Called when the badguy is drawn. The default implementation simply draws
+   * the badguy sprite on screen
+   */
+  virtual void draw(DrawingContext& context);
+  /** Called each frame. The default implementation checks badguy state and
+   * calls active_update and inactive_update
+   */
+  virtual void update(float elapsed_time);
+  /** Called when a collision with another object occured. The default
+   * implemetnation calls collision_player, collision_solid, collision_badguy
+   * and collision_squished
+   */
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  /** Called when a collision with tile with special attributes occured */
+  virtual void collision_tile(uint32_t tile_attributes);
+
+  /** Set the badguy to kill/falling state, which makes him falling of the
+   * screen (his sprite is turned upside-down)
+   */
+  virtual void kill_fall();
+  
+  /** Call this, if you use custom kill_fall() or kill_squashed(GameObject& object) */
+  virtual void run_dead_script();
+
+  /** Writes out the badguy into the included lisp::Writer. Useful e.g. when
+   * converting an old-format level to the new format.
+   */
+  virtual void write(lisp::Writer& writer);
+
+  /**
+   * True if this badguy can break bricks or open bonusblocks in his current form.
+   */
+  virtual bool can_break()
+  {
+    return false;
+  }
+
+  Vector get_start_position() const
+  {
+    return start_position;
+  }
+  void set_start_position(const Vector& vec)
+  {
+    start_position = vec;
+  }
+
+  /** Count this badguy to the statistics? This value should not be changed
+   * during runtime. */
+  bool countMe;
+
+  /**
+   * Called when hit by a fire bullet, and is_flammable() returns true
+   */
+  virtual void ignite();
+
+  /**
+   * Called to revert a badguy when is_ignited() returns true
+   */
+  virtual void extinguish();
+
+  /**
+   * Returns whether to call ignite() when a badguy gets hit by a fire bullet
+   */
+  virtual bool is_flammable() const;
+
+  /**
+   * Returns whether this badguys is currently on fire
+   */
+  bool is_ignited() const;
+
+  /**
+   * Called when hit by an ice bullet, and is_freezable() returns true.
+   */
+  virtual void freeze();
+
+  /**
+   * Called to unfreeze the badguy.
+   */
+  virtual void unfreeze();
+
+  virtual bool is_freezable() const;
+
+  bool is_frozen() const;
+
+protected:
+  enum State {
+    STATE_INIT,
+    STATE_INACTIVE,
+    STATE_ACTIVE,
+    STATE_SQUISHED,
+    STATE_FALLING
+  };
+
+  /** Called when the badguy collided with a player */
+  virtual HitResponse collision_player(Player& player, const CollisionHit& hit);
+  /** Called when the badguy collided with solid ground */
+  virtual void collision_solid(const CollisionHit& hit);
+  /** Called when the badguy collided with another badguy */
+  virtual HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+  /** Called when the player hit the badguy from above. You should return true
+   * if the badguy was squished, false if squishing wasn't possible
+   */
+  virtual bool collision_squished(GameObject& object);
+
+  /** Called when the badguy collided with a bullet */
+  virtual HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
+
+  /** called each frame when the badguy is activated. */
+  virtual void active_update(float elapsed_time);
+  /** called each frame when the badguy is not activated. */
+  virtual void inactive_update(float elapsed_time);
+
+  /**
+   * called when the badguy has been activated. (As a side effect the dir
+   * variable might have been changed so that it faces towards the player.
+   */
+  virtual void activate();
+  /** called when the badguy has been deactivated */
+  virtual void deactivate();
+
+  void kill_squished(GameObject& object);
+
+  void set_state(State state);
+  State get_state() const
+  { return state; }
+
+  /**
+   * returns a pointer to the nearest player or 0 if no player is available
+   */
+  Player* get_nearest_player();
+
+  /// is the enemy activated
+  bool activated;
+  /**
+   * initial position of the enemy. Also the position where enemy respawns when
+   * after being deactivated.
+   */
+  bool is_offscreen();
+  /**
+   *  Returns true if we might soon fall at least @c height pixels. Minimum
+   *  value for height is 1 pixel
+   */
+  bool might_fall(int height = 1);
+
+  Vector start_position;
+
+  /**
+   * The direction we currently face in
+   */
+  Direction dir;
+
+  /**
+   * The direction we initially faced in
+   */
+  Direction start_dir;
+
+  /**
+   *  Get Direction from String.
+   */
+  Direction str2dir( std::string dir_str );
+
+  /**
+   * Update on_ground_flag judging by solid collision @c hit.
+   * This gets called from the base implementation of collision_solid, so call this when overriding collision_solid's default behaviour.
+   */
+  void update_on_ground_flag(const CollisionHit& hit);
+
+  /**
+   * Returns true if we touched ground in the past frame
+   * This only works if update_on_ground_flag() gets called in collision_solid.
+   */
+  bool on_ground();
+
+  /**
+   * Returns floor normal stored the last time when update_on_ground_flag was called and we touched something solid from above.
+   */
+  Vector get_floor_normal();
+
+  bool frozen;
+  bool ignited; /**< true if this badguy is currently on fire */
+
+  std::string dead_script; /**< script to execute when badguy is killed */
+
+private:
+  void try_activate();
+
+  State state;
+  Timer state_timer;
+  bool on_ground_flag; /**< true if we touched something solid from above and update_on_ground_flag was called last frame */
+  Vector floor_normal; /**< floor normal stored the last time when update_on_ground_flag was called and we touched something solid from above */
+
+};
+
+#endif
diff --git a/src/badguy/bomb.cpp b/src/badguy/bomb.cpp
new file mode 100644 (file)
index 0000000..cbc96c0
--- /dev/null
@@ -0,0 +1,104 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "bomb.hpp"
+#include "random_generator.hpp"
+#include "object/explosion.hpp"
+
+Bomb::Bomb(const Vector& pos, Direction dir, std::string custom_sprite /*= "images/creatures/mr_bomb/mr_bomb.sprite"*/ )
+       : BadGuy( pos, dir, custom_sprite )
+{
+  state = STATE_TICKING;
+  set_action(dir == LEFT ? "ticking-left" : "ticking-right", 1);
+  countMe = false;
+
+  ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
+  ticking->set_position(get_pos());
+  ticking->set_looping(true);
+  ticking->set_gain(2.0);
+  ticking->set_reference_distance(32);
+  ticking->play();
+}
+
+Bomb::Bomb(const Bomb& other)
+       : BadGuy(other), state(other.state)
+{
+  if (state == STATE_TICKING) {
+    ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
+    ticking->set_position(get_pos());
+    ticking->set_looping(true);
+    ticking->set_gain(2.0);
+    ticking->set_reference_distance(32);
+    ticking->play();
+  }
+}
+
+void
+Bomb::write(lisp::Writer& )
+{
+  // bombs are only temporarily so don't write them out...
+}
+
+void
+Bomb::collision_solid(const CollisionHit& hit)
+{
+  if(hit.bottom)
+    physic.set_velocity_y(0);
+}
+
+HitResponse
+Bomb::collision_player(Player& , const CollisionHit& )
+{
+  return ABORT_MOVE;
+}
+
+HitResponse
+Bomb::collision_badguy(BadGuy& , const CollisionHit& )
+{
+  return ABORT_MOVE;
+}
+
+void
+Bomb::active_update(float )
+{
+  ticking->set_position(get_pos());
+  if(sprite->animation_done()) {
+    explode();
+  }
+}
+
+void
+Bomb::explode()
+{
+  ticking->stop();
+
+  remove_me();
+  Explosion* explosion = new Explosion(get_bbox().get_middle());
+  Sector::current()->add_object(explosion);
+
+  run_dead_script();
+}
+
+void
+Bomb::kill_fall()
+{
+  explode();
+}
diff --git a/src/badguy/bomb.hpp b/src/badguy/bomb.hpp
new file mode 100644 (file)
index 0000000..e75a32f
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BOMB_H__
+#define __BOMB_H__
+
+#include "badguy.hpp"
+
+class Bomb : public BadGuy
+{
+public:
+  Bomb(const Vector& pos, Direction dir, std::string custom_sprite = "images/creatures/mr_bomb/bomb.sprite" );
+  Bomb(const Bomb& bomb);
+
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  void active_update(float elapsed_time);
+  void kill_fall();
+  void explode();
+
+private:
+  enum State {
+    STATE_TICKING
+  };
+
+  State state;
+
+  std::auto_ptr<SoundSource> ticking;
+};
+
+#endif
diff --git a/src/badguy/bouncing_snowball.cpp b/src/badguy/bouncing_snowball.cpp
new file mode 100644 (file)
index 0000000..f84561b
--- /dev/null
@@ -0,0 +1,90 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "bouncing_snowball.hpp"
+
+static const float JUMPSPEED = -450;
+static const float WALKSPEED = 80;
+
+BouncingSnowball::BouncingSnowball(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/bouncing_snowball/bouncing_snowball.sprite")
+{
+}
+
+BouncingSnowball::BouncingSnowball(const Vector& pos, Direction d)
+  : BadGuy(pos, d, "images/creatures/bouncing_snowball/bouncing_snowball.sprite")
+{
+}
+
+void
+BouncingSnowball::write(lisp::Writer& writer)
+{
+  writer.start_list("bouncingsnowball");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("bouncingsnowball");
+}
+
+void
+BouncingSnowball::activate()
+{
+  physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+  sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+bool
+BouncingSnowball::collision_squished(GameObject& object)
+{
+  sprite->set_action("squished");
+  kill_squished(object);
+  return true;
+}
+
+void
+BouncingSnowball::collision_solid(const CollisionHit& hit)
+{
+  if(hit.bottom) {
+    if(get_state() == STATE_ACTIVE) {
+      physic.set_velocity_y(JUMPSPEED);
+    } else {
+      physic.set_velocity_y(0);
+    }
+  } else if(hit.top) {
+    physic.set_velocity_y(0);
+  }
+
+  if(hit.left || hit.right) { // left or right collision
+    dir = dir == LEFT ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(-physic.get_velocity_x());
+  }
+}
+
+HitResponse
+BouncingSnowball::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+  collision_solid(hit);
+  return CONTINUE;
+}
+
+IMPLEMENT_FACTORY(BouncingSnowball, "bouncingsnowball")
diff --git a/src/badguy/bouncing_snowball.hpp b/src/badguy/bouncing_snowball.hpp
new file mode 100644 (file)
index 0000000..ed2c5cb
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BOUNCING_SNOWBALL_H__
+#define __BOUNCING_SNOWBALL_H__
+
+#include "badguy.hpp"
+
+class BouncingSnowball : public BadGuy
+{
+public:
+  BouncingSnowball(const lisp::Lisp& reader);
+  BouncingSnowball(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+  virtual BouncingSnowball* clone() const { return new BouncingSnowball(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+};
+
+#endif
diff --git a/src/badguy/dart.cpp b/src/badguy/dart.cpp
new file mode 100644 (file)
index 0000000..393474e
--- /dev/null
@@ -0,0 +1,136 @@
+//  $Id$
+//
+//  Dart - Your average poison dart
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+
+#include "dart.hpp"
+
+namespace {
+  const float SPEED = 200;
+}
+
+static const std::string SOUNDFILE = "sounds/flame.wav";
+
+Dart::Dart(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/dart/dart.sprite"), parent(0)
+{
+  physic.enable_gravity(false);
+  countMe = false;
+  sound_manager->preload("sounds/darthit.wav");
+  sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::Dart(const Vector& pos, Direction d, const BadGuy* parent = 0)
+       : BadGuy(pos, d, "images/creatures/dart/dart.sprite"), parent(parent)
+{
+  physic.enable_gravity(false);
+  countMe = false;
+  sound_manager->preload("sounds/darthit.wav");
+  sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::Dart(const Dart& other)
+       : BadGuy(other), parent(other.parent)
+{
+  sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+  sound_manager->preload("sounds/darthit.wav");
+  sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::~Dart()
+{
+}
+
+bool
+Dart::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+  if (from_object == parent) {
+    parent = dynamic_cast<Dart*>(to_object);
+    return true;
+  }
+  return false;
+}
+
+void
+Dart::write(lisp::Writer& writer)
+{
+  writer.start_list("dart");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("dart");
+}
+
+void
+Dart::activate()
+{
+  physic.set_velocity_x(dir == LEFT ? -::SPEED : ::SPEED);
+  sprite->set_action(dir == LEFT ? "flying-left" : "flying-right");
+
+  sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+  sound_source->set_position(get_pos());
+  sound_source->set_looping(true);
+  sound_source->set_gain(1.0);
+  sound_source->set_reference_distance(32);
+  sound_source->play();
+}
+
+void
+Dart::deactivate()
+{
+  sound_source.reset();
+  remove_me();
+}
+
+void
+Dart::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+  sound_source->set_position(get_pos());
+}
+
+void
+Dart::collision_solid(const CollisionHit& )
+{
+  sound_manager->play("sounds/darthit.wav", get_pos());
+  remove_me();
+}
+
+HitResponse
+Dart::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+  // ignore collisions with parent
+  if (&badguy == parent) {
+    return FORCE_MOVE;
+  }
+  sound_manager->play("sounds/stomp.wav", get_pos());
+  remove_me();
+  badguy.kill_fall();
+  return ABORT_MOVE;
+}
+
+HitResponse
+Dart::collision_player(Player& player, const CollisionHit& hit)
+{
+  sound_manager->play("sounds/stomp.wav", get_pos());
+  remove_me();
+  return BadGuy::collision_player(player, hit);
+}
+
+IMPLEMENT_FACTORY(Dart, "dart")
diff --git a/src/badguy/dart.hpp b/src/badguy/dart.hpp
new file mode 100644 (file)
index 0000000..bbab1df
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  Dart - Your average poison dart
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __DART_H__
+#define __DART_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "Dart" - Your average poison dart
+ */
+class Dart : public BadGuy
+{
+public:
+  Dart(const lisp::Lisp& reader);
+  Dart(const Vector& pos, Direction d, const BadGuy* parent);
+  Dart(const Dart& dart);
+  ~Dart();
+
+  void activate();
+  void deactivate();
+  void write(lisp::Writer& writer);
+
+  void active_update(float elapsed_time);
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  virtual Dart* clone() const { return new Dart(*this); }
+
+  virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+  const BadGuy* parent; /**< collisions with this BadGuy will be ignored */
+  std::auto_ptr<SoundSource> sound_source; /**< SoundSource for ambient sound */
+};
+
+#endif
diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp
new file mode 100644 (file)
index 0000000..7c6a84b
--- /dev/null
@@ -0,0 +1,107 @@
+//  $Id$
+//
+//  DartTrap - Shoots a Dart at regular intervals
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "darttrap.hpp"
+#include "dart.hpp"
+
+namespace {
+  const float MUZZLE_Y = 25; /**< [px] muzzle y-offset from top */
+}
+
+DartTrap::DartTrap(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/darttrap/darttrap.sprite", LAYER_TILES-1), initial_delay(0), fire_delay(2), ammo(-1), state(IDLE)
+{
+  reader.get("initial-delay", initial_delay);
+  reader.get("fire-delay", fire_delay);
+  reader.get("ammo", ammo);
+  countMe = false;
+  sound_manager->preload("sounds/dartfire.wav");
+  if (start_dir == AUTO) log_warning << "Setting a DartTrap's direction to AUTO is no good idea" << std::endl;
+}
+
+void
+DartTrap::write(lisp::Writer& writer)
+{
+  writer.start_list("darttrap");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.write_float("initial-delay", initial_delay);
+  writer.write_float("fire-delay", fire_delay);
+  writer.write_int("ammo", ammo);
+  writer.end_list("darttrap");
+}
+
+void
+DartTrap::activate()
+{
+  state = IDLE;
+  sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+  set_group(COLGROUP_DISABLED);
+
+  if (initial_delay == 0) initial_delay = 0.1f;
+  fire_timer.start(initial_delay);
+}
+
+HitResponse
+DartTrap::collision_player(Player& , const CollisionHit& )
+{
+  return ABORT_MOVE;
+}
+
+void
+DartTrap::active_update(float )
+{
+  if (state == IDLE) {
+    if ((ammo != 0) && (fire_timer.check())) {
+      if (ammo > 0) ammo--;
+      load();
+      fire_timer.start(fire_delay);
+    }
+  }
+  if (state == LOADING) {
+    if (sprite->animation_done()) {
+      fire();
+    }
+  }
+}
+
+void
+DartTrap::load()
+{
+  state = LOADING;
+  sprite->set_action(dir == LEFT ? "loading-left" : "loading-right", 1);
+}
+
+void
+DartTrap::fire()
+{
+  float px = get_pos().x;
+  if (dir == RIGHT) px += 5;
+  float py = get_pos().y;
+  py += MUZZLE_Y;
+
+  sound_manager->play("sounds/dartfire.wav", get_pos());
+  Sector::current()->add_object(new Dart(Vector(px, py), dir, this));
+  state = IDLE;
+  sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+}
+
+IMPLEMENT_FACTORY(DartTrap, "darttrap")
diff --git a/src/badguy/darttrap.hpp b/src/badguy/darttrap.hpp
new file mode 100644 (file)
index 0000000..7f2b80e
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  DartTrap - Shoots a Dart at regular intervals
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __DARTTRAP_H__
+#define __DARTTRAP_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+/**
+ * Badguy "DartTrap" - Shoots a Dart at regular intervals
+ */
+class DartTrap : public BadGuy
+{
+public:
+  DartTrap(const lisp::Lisp& reader);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void active_update(float elapsed_time);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  virtual DartTrap* clone() const { return new DartTrap(*this); }
+
+protected:
+  enum State {
+    IDLE, LOADING
+  };
+
+  void load(); /**< load a shot */
+  void fire(); /**< fire a shot */
+
+  float initial_delay; /**< time to wait before firing first shot */
+  float fire_delay; /**< reload time */
+  int ammo; /**< ammo left (-1 means unlimited) */
+
+  State state; /**< current state */
+  Timer fire_timer; /**< time until new shot is fired */
+};
+
+#endif
diff --git a/src/badguy/dispenser.cpp b/src/badguy/dispenser.cpp
new file mode 100644 (file)
index 0000000..123e4aa
--- /dev/null
@@ -0,0 +1,153 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "dispenser.hpp"
+#include "badguy/bouncing_snowball.hpp"
+#include "badguy/snowball.hpp"
+#include "badguy/mrbomb.hpp"
+#include "badguy/mriceblock.hpp"
+#include "badguy/mrrocket.hpp"
+#include "badguy/poisonivy.hpp"
+#include "badguy/snail.hpp"
+#include "badguy/skullyhop.hpp"
+#include "random_generator.hpp"
+
+Dispenser::Dispenser(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/dispenser/dispenser.sprite")
+{
+  reader.get("cycle", cycle);
+  reader.get("badguy", badguy);
+  if (badguy == "mrrocket") {
+     if (start_dir == AUTO) log_warning << "Setting a Dispenser's direction to AUTO is no good idea" << std::endl;
+     sprite->set_action(dir == LEFT ? "working-left" : "working-right");
+  }
+  else {sprite->set_action("dropper");}
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  countMe = false;
+}
+
+void
+Dispenser::write(lisp::Writer& writer)
+{
+  writer.start_list("dispenser");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.write_float("cycle", cycle);
+  writer.write_string("badguy", badguy);
+
+  writer.end_list("dispenser");
+}
+
+void
+Dispenser::activate()
+{
+   if(frozen)
+     return;
+   dispense_timer.start(cycle, true);
+   launch_badguy();
+}
+
+void
+Dispenser::deactivate()
+{
+   dispense_timer.stop();
+}
+
+//TODO: Add launching velocity to certain badguys
+bool
+Dispenser::collision_squished(GameObject& object)
+{
+  //TODO: Should it act like a normal tile when killed?
+  sprite->set_action(dir == LEFT ? "broken-left" : "broken-right");
+  dispense_timer.start(0);
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) player->bounce(*this);
+  kill_squished(object);
+  return true;
+}
+
+void
+Dispenser::active_update(float )
+{
+  if (dispense_timer.check()) {
+    launch_badguy();
+  }
+}
+
+//      Add themed randomizer
+void
+Dispenser::launch_badguy()
+{
+  //FIXME: Does is_offscreen() work right here?
+  if (!is_offscreen()) {
+    if (badguy == "snowball")
+      Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "bouncingsnowball")
+      Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "mrbomb")
+      Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "mriceblock")
+      Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "snail")
+      Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "mrrocket") {
+      Sector::current()->add_object(new MrRocket(Vector(get_pos().x+(dir == LEFT ? -32 : 32), get_pos().y), dir));}
+    else if (badguy == "poisonivy")
+      Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), dir));
+    else if (badguy == "skullyhop")
+      Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), dir));
+    else if (badguy == "random")
+    {
+      switch (systemRandom.rand(7))
+      {
+        case 0: Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 1: Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 2: Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 3: Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 4: Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 5: Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), dir)); break;
+        case 6: Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), dir)); break;
+      }
+    }
+  }
+}
+
+void
+Dispenser::freeze()
+{
+  BadGuy::freeze();
+  dispense_timer.stop();
+}
+
+void
+Dispenser::unfreeze()
+{
+  BadGuy::unfreeze();
+  activate();
+}
+
+bool
+Dispenser::is_freezable() const
+{
+  return true;
+}
+IMPLEMENT_FACTORY(Dispenser, "dispenser")
diff --git a/src/badguy/dispenser.hpp b/src/badguy/dispenser.hpp
new file mode 100644 (file)
index 0000000..e553d54
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __DISPENSER_H__
+#define __DISPENSER_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+class Dispenser : public BadGuy
+{
+public:
+  Dispenser(const lisp::Lisp& reader);
+
+  void activate();
+  void deactivate();
+  void write(lisp::Writer& writer);
+  void active_update(float elapsed_time);
+
+  void freeze();
+  void unfreeze();
+  bool is_freezable() const;
+
+  virtual Dispenser* clone() const { return new Dispenser(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+  void launch_badguy();
+  float cycle;
+  std::string badguy;
+  Timer dispense_timer;
+};
+
+#endif
diff --git a/src/badguy/fish.cpp b/src/badguy/fish.cpp
new file mode 100644 (file)
index 0000000..8035120
--- /dev/null
@@ -0,0 +1,162 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "fish.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "log.hpp"
+
+static const float FISH_JUMP_POWER = -600;
+static const float FISH_WAIT_TIME = 1;
+
+Fish::Fish(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/fish/fish.sprite", LAYER_TILES-1), stop_y(0)
+{
+  physic.enable_gravity(true);
+}
+
+Fish::Fish(const Vector& pos)
+       : BadGuy(pos, "images/creatures/fish/fish.sprite", LAYER_TILES-1), stop_y(0)
+{
+  physic.enable_gravity(true);
+}
+
+void
+Fish::write(lisp::Writer& writer)
+{
+  writer.start_list("fish");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("fish");
+}
+
+void
+Fish::collision_solid(const CollisionHit& chit)
+{
+  hit(chit);
+}
+
+HitResponse
+Fish::collision_badguy(BadGuy& , const CollisionHit& chit)
+{
+  return hit(chit);
+}
+
+void
+Fish::draw(DrawingContext& context)
+{
+  if(waiting.started())
+    return;
+
+  BadGuy::draw(context);
+}
+
+HitResponse
+Fish::hit(const CollisionHit& hit)
+{
+  if(hit.top) {
+    physic.set_velocity_y(0);
+  }
+
+  return CONTINUE;
+}
+
+void
+Fish::collision_tile(uint32_t tile_attributes)
+{
+  if ((tile_attributes & Tile::WATER) && (physic.get_velocity_y() >= 0)) {
+
+    // initialize stop position if uninitialized
+    if (stop_y == 0) stop_y = get_pos().y + get_bbox().get_height();
+
+    // stop when we have reached the stop position
+    if (get_pos().y >= stop_y) {
+      if(!frozen)
+        start_waiting();
+      movement = Vector(0, 0);
+    }
+
+  }
+}
+
+void
+Fish::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  // waited long enough?
+  if(waiting.check()) {
+    jump();
+  }
+
+  // set sprite
+  if(!frozen)
+    sprite->set_action(physic.get_velocity_y() < 0 ? "normal" : "down");
+
+  // we can't afford flying out of the tilemap, 'cause the engine would remove us.
+  if ((get_pos().y - 31.8) < 0) // too high, let us fall
+  {
+    physic.set_velocity_y(0);
+    physic.enable_gravity(true);
+  }
+}
+
+void
+Fish::start_waiting()
+{
+  waiting.start(FISH_WAIT_TIME);
+  set_group(COLGROUP_DISABLED);
+  physic.enable_gravity(false);
+  physic.set_velocity_y(0);
+}
+
+void
+Fish::jump()
+{
+  physic.set_velocity_y(FISH_JUMP_POWER);
+  physic.enable_gravity(true);
+  set_group(COLGROUP_MOVING);
+}
+
+void
+Fish::freeze()
+{
+  BadGuy::freeze();
+  sprite->set_action(physic.get_velocity_y() < 0 ? "iced" : "iced-down");
+  waiting.stop();
+}
+
+void
+Fish::unfreeze()
+{ // does this happen at all? (or do fishes die when they fall frozen?)
+  BadGuy::unfreeze();
+  start_waiting();
+}
+
+bool
+Fish::is_freezable() const
+{
+  return true;
+}
+
+IMPLEMENT_FACTORY(Fish, "fish")
diff --git a/src/badguy/fish.hpp b/src/badguy/fish.hpp
new file mode 100644 (file)
index 0000000..fa36dee
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FISH_H__
+#define __FISH_H__
+
+#include "badguy.hpp"
+
+class Fish : public BadGuy
+{
+public:
+  Fish(const lisp::Lisp& );
+  Fish(const Vector& pos);
+
+  void draw(DrawingContext& context);
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& , const CollisionHit& );
+  void collision_tile(uint32_t tile_attributes);
+
+  void write(lisp::Writer& );
+  void active_update(float);
+
+  void freeze();
+  void unfreeze();
+  bool is_freezable() const;
+
+  virtual Fish* clone() const { return new Fish(*this); }
+
+private:
+  HitResponse hit(const CollisionHit& );
+  void start_waiting();
+  void jump();
+
+  Timer waiting;
+  float stop_y; /**< y-coordinate to stop at */
+};
+
+#endif
diff --git a/src/badguy/flame.cpp b/src/badguy/flame.cpp
new file mode 100644 (file)
index 0000000..7f8d356
--- /dev/null
@@ -0,0 +1,86 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "flame.hpp"
+#include "log.hpp"
+
+static const std::string SOUNDFILE = "sounds/flame.wav";
+
+Flame::Flame(const lisp::Lisp& reader)
+  : BadGuy(reader, "images/creatures/flame/flame.sprite", LAYER_FLOATINGOBJECTS), angle(0), radius(100), speed(2)
+{
+  reader.get("radius", radius);
+  reader.get("speed", speed);
+  bbox.set_pos(Vector(start_position.x + cos(angle) * radius,
+                      start_position.y + sin(angle) * radius));
+  countMe = false;
+  sound_manager->preload(SOUNDFILE);
+}
+
+void
+Flame::write(lisp::Writer& writer)
+{
+  writer.start_list("flame");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.write_float("radius", radius);
+  writer.write_float("speed", speed);
+
+  writer.end_list("flame");
+}
+
+void
+Flame::active_update(float elapsed_time)
+{
+  angle = fmodf(angle + elapsed_time * speed, (float) (2*M_PI));
+  Vector newpos(start_position.x + cos(angle) * radius,
+                start_position.y + sin(angle) * radius);
+  movement = newpos - get_pos();
+
+  sound_source->set_position(get_pos());
+}
+
+void
+Flame::activate()
+{
+  set_group(COLGROUP_TOUCHABLE);
+
+  sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+  sound_source->set_position(get_pos());
+  sound_source->set_looping(true);
+  sound_source->set_gain(2.0);
+  sound_source->set_reference_distance(32);
+  sound_source->play();
+}
+
+void
+Flame::deactivate()
+{
+  sound_source.reset();
+}
+
+void
+Flame::kill_fall()
+{
+}
+
+IMPLEMENT_FACTORY(Flame, "flame")
diff --git a/src/badguy/flame.hpp b/src/badguy/flame.hpp
new file mode 100644 (file)
index 0000000..ac8a8fc
--- /dev/null
@@ -0,0 +1,45 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __FLAME_H__
+#define __FLAME_H__
+
+#include "badguy.hpp"
+
+class Flame : public BadGuy
+{
+public:
+  Flame(const lisp::Lisp& reader);
+  Flame(const Flame& flame);
+
+  void activate();
+  void deactivate();
+
+  void write(lisp::Writer& write);
+  void active_update(float elapsed_time);
+  void kill_fall();
+
+private:
+  float angle;
+  float radius;
+  float speed;
+
+  std::auto_ptr<SoundSource> sound_source;
+};
+
+#endif
diff --git a/src/badguy/flyingsnowball.cpp b/src/badguy/flyingsnowball.cpp
new file mode 100644 (file)
index 0000000..a0abf23
--- /dev/null
@@ -0,0 +1,127 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include "flyingsnowball.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+static const float FLYTIME = 1.0f;
+static const float FLYSPEED = -100.0f;
+
+namespace {
+  const float PUFF_PROBABILITY = 0.1f; /**< chanche of puffs being spawned in the current cycle */
+  const float PUFF_INTERVAL_MIN = 0.1f; /**< spawn new puff of smoke at most that often */
+  const float PUFF_INTERVAL_MAX = 1.1f; /**< spawn new puff of smoke at least that often */
+}
+
+FlyingSnowBall::FlyingSnowBall(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/flying_snowball/flying_snowball.sprite")
+{
+  physic.enable_gravity(false);
+}
+
+FlyingSnowBall::FlyingSnowBall(const Vector& pos)
+       : BadGuy(pos, "images/creatures/flying_snowball/flying_snowball.sprite")
+{
+  physic.enable_gravity(false);
+}
+
+void
+FlyingSnowBall::write(lisp::Writer& writer)
+{
+  writer.start_list("flyingsnowball");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("flyingsnowball");
+}
+
+void
+FlyingSnowBall::activate()
+{
+  sprite->set_action(dir == LEFT ? "left" : "right");
+  mode = FLY_UP;
+  physic.set_velocity_y(FLYSPEED);
+  timer.start(FLYTIME/2);
+  puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+}
+
+bool
+FlyingSnowBall::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+void
+FlyingSnowBall::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    physic.set_velocity_y(0);
+  }
+}
+
+void
+FlyingSnowBall::active_update(float elapsed_time)
+{
+  if(timer.check()) {
+    if(mode == FLY_UP) {
+      mode = FLY_DOWN;
+      physic.set_velocity_y(-FLYSPEED);
+
+      // stop puffing
+      puff_timer.stop();
+
+    } else if(mode == FLY_DOWN) {
+      mode = FLY_UP;
+      physic.set_velocity_y(FLYSPEED);
+
+      // roll a dice whether to start puffing
+      if (systemRandom.randf(0, 1) < PUFF_PROBABILITY) {
+        puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+      }
+
+    }
+    timer.start(FLYTIME);
+  }
+  movement=physic.get_movement(elapsed_time);
+
+  Player* player = this->get_nearest_player();
+  if (player) {
+    dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+  }
+
+  // spawn smoke puffs
+  if (puff_timer.check()) {
+    Vector ppos = bbox.get_middle();
+    Vector pspeed = Vector(systemRandom.randf(-10, 10), 150);
+    Vector paccel = Vector(0,0);
+    Sector::current()->add_object(new SpriteParticle("images/objects/particles/smoke.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+    puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+  }
+}
+
+IMPLEMENT_FACTORY(FlyingSnowBall, "flyingsnowball")
diff --git a/src/badguy/flyingsnowball.hpp b/src/badguy/flyingsnowball.hpp
new file mode 100644 (file)
index 0000000..43e9324
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FLYINGSNOWBALL_H__
+#define __FLYINGSNOWBALL_H__
+
+#include "badguy.hpp"
+
+class FlyingSnowBall : public BadGuy
+{
+public:
+  FlyingSnowBall(const lisp::Lisp& reader);
+  FlyingSnowBall(const Vector& pos);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void active_update(float elapsed_time);
+  void collision_solid(const CollisionHit& hit);
+
+  virtual FlyingSnowBall* clone() const { return new FlyingSnowBall(*this); }
+
+protected:
+  enum FlyingSnowballMode {
+    FLY_UP,
+    FLY_DOWN
+  };
+  FlyingSnowballMode mode;
+  bool collision_squished(GameObject& object);
+private:
+  Timer timer;
+  Timer puff_timer; /**< time until the next smoke puff is spawned */
+};
+
+#endif
diff --git a/src/badguy/ghosttree.cpp b/src/badguy/ghosttree.cpp
new file mode 100644 (file)
index 0000000..973afa8
--- /dev/null
@@ -0,0 +1,253 @@
+//  $Id$
+//
+//  SuperTux - Boss "GhostTree"
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "ghosttree.hpp"
+#include "treewillowisp.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "root.hpp"
+#include "random_generator.hpp"
+#include "object/lantern.hpp"
+
+static const size_t WILLOWISP_COUNT = 10;
+static const float ROOT_TOP_OFFSET = 64;
+static const float WILLOWISP_TOP_OFFSET = -64;
+static const Vector SUCK_TARGET_OFFSET = Vector(-16,-16);
+static const float SUCK_TARGET_SPREAD = 8;
+
+GhostTree::GhostTree(const lisp::Lisp& lisp)
+  : BadGuy(lisp, "images/creatures/ghosttree/ghosttree.sprite",
+           LAYER_OBJECTS - 10), mystate(STATE_IDLE),
+    willo_spawn_y(0), willo_radius(200), willo_speed(1.8f), willo_color(0),
+    treecolor(0), suck_lantern(0)
+{
+  glow_sprite.reset(sprite_manager->create("images/creatures/ghosttree/ghosttree-glow.sprite"));
+}
+
+GhostTree::~GhostTree()
+{
+}
+
+void
+GhostTree::die()
+{
+  mystate = STATE_DYING;
+  sprite->set_action("dying", 1); 
+  glow_sprite->set_action("dying", 1); 
+
+  std::vector<TreeWillOWisp*>::iterator iter;
+  for(iter = willowisps.begin(); iter != willowisps.end(); ++iter) {
+    TreeWillOWisp *willo = *iter;
+    willo->vanish();
+  }
+}
+
+void
+GhostTree::activate()
+{
+  willowisp_timer.start(1.0f, true);
+  colorchange_timer.start(13, true);
+  root_timer.start(5, true);
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+GhostTree::active_update(float elapsed_time)
+{
+  (void) elapsed_time;
+
+  if (mystate == STATE_IDLE) {
+    if(colorchange_timer.check()) {
+      sound_manager->play("sounds/tree_howling.ogg", get_pos());
+      suck_timer.start(3);
+      treecolor = (treecolor + 1) % 3;
+
+      Color col;
+      switch(treecolor) {
+       case 0: col = Color(1, 0, 0); break;
+       case 1: col = Color(0, 1, 0); break;
+       case 2: col = Color(0, 0, 1); break;
+       case 3: col = Color(1, 1, 0); break;
+       case 4: col = Color(1, 0, 1); break;
+       case 5: col = Color(0, 1, 1); break;
+       default: assert(false);
+      }
+      glow_sprite->set_color(col);
+    }
+
+    if(suck_timer.check()) {
+      Color col = glow_sprite->get_color();
+      sound_manager->play("sounds/tree_suck.ogg", get_pos());
+      std::vector<TreeWillOWisp*>::iterator iter;
+      for(iter = willowisps.begin(); iter != willowisps.end(); ++iter) {
+       TreeWillOWisp *willo = *iter;
+       if(willo->get_color() == col) {
+         willo->start_sucking(get_bbox().get_middle() + SUCK_TARGET_OFFSET + Vector(systemRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD), systemRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD)));
+       }
+      }
+      mystate = STATE_SUCKING;
+    }
+
+    if(willowisp_timer.check()) {
+      if(willowisps.size() < WILLOWISP_COUNT) {
+       Vector pos = Vector(bbox.get_width() / 2, bbox.get_height() / 2 + willo_spawn_y + WILLOWISP_TOP_OFFSET);
+       TreeWillOWisp *willowisp 
+               = new TreeWillOWisp(this, pos, 200 + willo_radius, willo_speed);
+
+       Sector::current()->add_object(willowisp);
+       willowisps.push_back(willowisp);
+
+       willo_spawn_y -= 40;
+       if(willo_spawn_y < -160)
+         willo_spawn_y = 0;
+
+       willo_radius += 20;
+       if(willo_radius > 120)
+         willo_radius = 0;
+
+       if(willo_speed == 1.8f) {
+         willo_speed = 1.5f;
+       } else {
+         willo_speed = 1.8f;
+       }
+
+       do {
+         willo_color = (willo_color + 1) % 3;
+       } while(willo_color == treecolor);
+
+       switch(willo_color) {
+         case 0: willowisp->set_color(Color(1, 0, 0)); break;
+         case 1: willowisp->set_color(Color(0, 1, 0)); break;
+         case 2: willowisp->set_color(Color(0, 0, 1)); break;
+         case 3: willowisp->set_color(Color(1, 1, 0)); break;
+         case 4: willowisp->set_color(Color(1, 0, 1)); break;
+         case 5: willowisp->set_color(Color(0, 1, 1)); break;
+         default: assert(false);
+       }
+      }
+    }
+
+    if(root_timer.check()) {
+      /* TODO indicate root with an animation */
+      Player* player = get_nearest_player();
+      Root* root = new Root(Vector(player->get_bbox().get_left(), get_bbox().get_bottom()+ROOT_TOP_OFFSET));
+      Sector::current()->add_object(root);
+    }
+  } else if (mystate == STATE_SWALLOWING) {
+    if (suck_lantern) {
+      // suck in lantern
+      assert (suck_lantern);
+      Vector pos = suck_lantern->get_pos();
+      Vector delta = get_bbox().get_middle() + SUCK_TARGET_OFFSET - pos;
+      Vector dir = delta.unit();
+      if (delta.norm() < 1) {
+        dir = delta;
+        suck_lantern->ungrab(*this, RIGHT);
+        suck_lantern->remove_me();
+        suck_lantern = 0;
+        sprite->set_action("swallow", 1); 
+      } else {
+        pos += dir;
+        suck_lantern->grab(*this, pos, RIGHT);
+      }
+    } else {
+      // wait until lantern is swallowed
+      if (sprite->animation_done()) {
+        if (is_color_deadly(suck_lantern_color)) {
+          die();
+        } else {
+          sprite->set_action("default");
+          mystate = STATE_IDLE;
+          spawn_lantern();
+        }
+      }
+    }
+  }
+}
+
+bool 
+GhostTree::is_color_deadly(Color color) const {
+  if (color == Color(0,0,0)) return false;
+  Color my_color = glow_sprite->get_color();
+  return ((my_color.red != color.red) || (my_color.green != color.green) || (my_color.blue != color.blue));
+}
+
+void
+GhostTree::willowisp_died(TreeWillOWisp *willowisp)
+{
+  if ((mystate == STATE_SUCKING) && (willowisp->was_sucked)) {
+    mystate = STATE_IDLE;
+  }
+  willowisps.erase(std::find(willowisps.begin(), willowisps.end(), willowisp));
+}
+
+void
+GhostTree::draw(DrawingContext& context)
+{
+  BadGuy::draw(context);
+
+  context.push_target();
+  context.push_transform();
+  context.set_target(DrawingContext::LIGHTMAP);
+  if (mystate == STATE_SUCKING) {
+    context.set_alpha(0.5 + fmodf(game_time, 0.5));
+  } else {
+    context.set_alpha(0.5);
+  }
+  glow_sprite->draw(context, get_pos(), layer);
+  context.pop_transform();
+  context.pop_target();
+}
+
+bool
+GhostTree::collides(GameObject& other, const CollisionHit& ) {
+  if (mystate != STATE_SUCKING) return false;
+  if (dynamic_cast<Lantern*>(&other)) return true;
+  if (dynamic_cast<Player*>(&other)) return true;
+  return false;
+}
+
+HitResponse
+GhostTree::collision(GameObject& other, const CollisionHit& ) {
+  if(mystate != STATE_SUCKING) return ABORT_MOVE;
+
+  Player* player = dynamic_cast<Player*>(&other);
+  if (player) {
+    player->kill(false);
+  }
+
+  Lantern* lantern = dynamic_cast<Lantern*>(&other);
+  if (lantern) {
+    suck_lantern = lantern;
+    suck_lantern->grab(*this, suck_lantern->get_pos(), RIGHT);
+    suck_lantern_color = lantern->get_color();
+    mystate = STATE_SWALLOWING;
+  }
+
+  return ABORT_MOVE;
+}
+
+void
+GhostTree::spawn_lantern() {
+  Lantern* lantern = new Lantern(get_bbox().get_middle() + SUCK_TARGET_OFFSET);
+  Sector::current()->add_object(lantern);
+}
+
+IMPLEMENT_FACTORY(GhostTree, "ghosttree");
+
diff --git a/src/badguy/ghosttree.hpp b/src/badguy/ghosttree.hpp
new file mode 100644 (file)
index 0000000..7e57d5c
--- /dev/null
@@ -0,0 +1,75 @@
+//  $Id$
+//
+//  SuperTux - Boss "GhostTree"
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __GHOSTTREE_H__
+#define __GHOSTTREE_H__
+
+#include <vector>
+#include "badguy.hpp"
+
+class TreeWillOWisp;
+class Lantern;
+
+class GhostTree : public BadGuy
+{
+public:
+  GhostTree(const lisp::Lisp& lisp);
+  ~GhostTree();
+
+  virtual bool is_flammable() const { return false; }
+  virtual bool is_freezable() const { return false; }
+  virtual void kill_fall() { }
+
+  void activate();
+  void active_update(float elapsed_time);
+  void willowisp_died(TreeWillOWisp* willowisp);
+  virtual void draw(DrawingContext& context);
+
+  virtual bool collides(GameObject& other, const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  void die();
+
+private:
+  enum MyState {
+    STATE_IDLE, STATE_SUCKING, STATE_SWALLOWING, STATE_DYING
+  };
+  MyState mystate;
+  Timer willowisp_timer;
+  float willo_spawn_y;
+  float willo_radius;
+  float willo_speed;
+  int   willo_color;
+
+  std::auto_ptr<Sprite> glow_sprite;
+  Timer colorchange_timer;
+  Timer suck_timer;
+  Timer root_timer;
+  int   treecolor;
+  Color suck_lantern_color;
+
+  Lantern* suck_lantern; /**< Lantern that is currently being sucked in */
+
+  std::vector<TreeWillOWisp*> willowisps;
+
+  bool is_color_deadly(Color color) const;
+  void spawn_lantern();
+};
+
+#endif
+
diff --git a/src/badguy/igel.cpp b/src/badguy/igel.cpp
new file mode 100644 (file)
index 0000000..b8450d2
--- /dev/null
@@ -0,0 +1,127 @@
+//  $Id$
+//
+//  SuperTux - Badguy "Igel"
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "igel.hpp"
+#include "object/block.hpp"
+#include "sector.hpp"
+#include "object/bullet.hpp"
+
+namespace {
+  const float WALKSPEED = 80; /**< speed at which we walk around */
+  const float TURN_RECOVER_TIME = 0.5; /**< seconds before we will again turn around when shot at */
+  const float RANGE_OF_VISION = 256; /**< range in px at which we can see bullets */
+}
+
+Igel::Igel(const lisp::Lisp& reader)
+  : WalkingBadguy(reader, "images/creatures/igel/igel.sprite", "walking-left", "walking-right")
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = 16;
+}
+
+Igel::Igel(const Vector& pos, Direction d)
+  : WalkingBadguy(pos, d, "images/creatures/igel/igel.sprite", "walking-left", "walking-right")
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = 16;
+}
+
+void
+Igel::write(lisp::Writer& writer)
+{
+  writer.start_list("igel");
+  WalkingBadguy::write(writer);
+  writer.end_list("igel");
+}
+
+void
+Igel::be_normal()
+{
+  activate();
+}
+
+void
+Igel::turn_around()
+{
+  WalkingBadguy::turn_around();
+  turn_recover_timer.start(TURN_RECOVER_TIME);
+}
+
+bool
+Igel::can_see(const MovingObject& o)
+{
+  Rect mb = get_bbox();
+  Rect ob = o.get_bbox();
+
+  bool inReach_left = ((ob.p2.x < mb.p1.x) && (ob.p2.x >= mb.p1.x-((dir == LEFT) ? RANGE_OF_VISION : 0)));
+  bool inReach_right = ((ob.p1.x > mb.p2.x) && (ob.p1.x <= mb.p2.x+((dir == RIGHT) ? RANGE_OF_VISION : 0)));
+  bool inReach_top = (ob.p2.y >= mb.p1.y);
+  bool inReach_bottom = (ob.p1.y <= mb.p2.y);
+
+  return ((inReach_left || inReach_right) && inReach_top && inReach_bottom);
+}
+
+void
+Igel::active_update(float elapsed_time)
+{
+  bool wants_to_flee = false;
+
+  // check if we see a fire bullet
+  Sector* sector = Sector::current();
+  for (Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) {
+    Bullet* bullet = dynamic_cast<Bullet*>(*i);
+    if (!bullet) continue;
+    if (bullet->get_type() != FIRE_BONUS) continue;
+    if (can_see(*bullet)) wants_to_flee = true;
+  }
+
+  // if we flee, handle this ourselves
+  if (wants_to_flee && (!turn_recover_timer.started())) {
+    turn_around();
+    BadGuy::active_update(elapsed_time);
+    return;
+  }
+
+  // else adhere to default behaviour
+  WalkingBadguy::active_update(elapsed_time);
+}
+
+HitResponse
+Igel::collision_bullet(Bullet& bullet, const CollisionHit& hit)
+{
+  // default reaction if hit on front side
+  if (((dir == LEFT) && hit.left) || ((dir == RIGHT) && hit.right)) {
+    return BadGuy::collision_bullet(bullet, hit);
+  }
+
+  // else make bullet ricochet and ignore the hit
+  bullet.ricochet(*this, hit);
+  return FORCE_MOVE;
+}
+
+bool
+Igel::collision_squished(GameObject& )
+{
+  // this will hurt
+  return false;
+}
+
+IMPLEMENT_FACTORY(Igel, "igel")
diff --git a/src/badguy/igel.hpp b/src/badguy/igel.hpp
new file mode 100644 (file)
index 0000000..4d2b82f
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux - Badguy "Igel"
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __IGEL_H__
+#define __IGEL_H__
+
+#include "walking_badguy.hpp"
+#include "moving_object.hpp"
+
+/**
+ * Badguy "Igel" - a hedgehog that can absorb bullets
+ */
+class Igel : public WalkingBadguy
+{
+public:
+  Igel(const lisp::Lisp& reader);
+  Igel(const Vector& pos, Direction d);
+
+  void write(lisp::Writer& writer);
+  HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
+
+  void active_update(float elapsed_time);
+
+  virtual Igel* clone() const { return new Igel(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+  void be_normal(); /**< switch to state STATE_NORMAL */
+  void turn_around(); /**< reverse direction, assumes we are in STATE_NORMAL */
+  bool can_see(const MovingObject& o); /**< check if we can see o */
+
+private:
+  Timer turn_recover_timer; /**< wait time until we will turn around again when shot at */
+
+};
+
+#endif
diff --git a/src/badguy/jumpy.cpp b/src/badguy/jumpy.cpp
new file mode 100644 (file)
index 0000000..706a7d7
--- /dev/null
@@ -0,0 +1,119 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "jumpy.hpp"
+
+static const float JUMPSPEED=-600;
+static const float JUMPY_MID_TOLERANCE=4;
+static const float JUMPY_LOW_TOLERANCE=2;
+
+Jumpy::Jumpy(const lisp::Lisp& reader)
+    : BadGuy(reader, "images/creatures/jumpy/jumpy.sprite"), groundhit_pos_set(false)
+{
+}
+
+void
+Jumpy::write(lisp::Writer& writer)
+{
+  writer.start_list("jumpy");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("jumpy");
+}
+
+void
+Jumpy::collision_solid(const CollisionHit& chit)
+{
+  hit(chit);
+}
+
+HitResponse
+Jumpy::collision_badguy(BadGuy& , const CollisionHit& chit)
+{
+  return hit(chit);
+}
+
+HitResponse
+Jumpy::hit(const CollisionHit& chit)
+{
+  if(chit.bottom) {
+    if (!groundhit_pos_set)
+    {
+      pos_groundhit = get_pos();
+      groundhit_pos_set = true;
+    }
+
+    physic.set_velocity_y(frozen ? 0 : JUMPSPEED);
+    // TODO create a nice sound for this...
+    //sound_manager->play("sounds/skid.wav");
+  } else if(chit.top) {
+    physic.set_velocity_y(0);
+  }
+
+  return CONTINUE;
+}
+
+void
+Jumpy::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  if(frozen)
+    return;
+
+  Player* player = this->get_nearest_player();
+  if (player)
+  {
+    dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+  }
+
+  if (!groundhit_pos_set)
+  {
+    sprite->set_action(dir == LEFT ? "left-middle" : "right-middle");
+    return;
+  }
+
+  if ( get_pos().y < (pos_groundhit.y - JUMPY_MID_TOLERANCE ) )
+    sprite->set_action(dir == LEFT ? "left-up" : "right-up");
+  else if ( get_pos().y >= (pos_groundhit.y - JUMPY_MID_TOLERANCE) &&
+      get_pos().y < (pos_groundhit.y - JUMPY_LOW_TOLERANCE) )
+    sprite->set_action(dir == LEFT ? "left-middle" : "right-middle");
+  else
+    sprite->set_action(dir == LEFT ? "left-down" : "right-down");
+}
+
+void
+Jumpy::freeze()
+{
+  BadGuy::freeze();
+  physic.set_velocity_y(std::max(0.0f, physic.get_velocity_y()));
+  sprite->set_action(dir == LEFT ? "left-iced" : "right-iced");
+}
+
+bool
+Jumpy::is_freezable() const
+{
+  return true;
+}
+
+IMPLEMENT_FACTORY(Jumpy, "jumpy")
diff --git a/src/badguy/jumpy.hpp b/src/badguy/jumpy.hpp
new file mode 100644 (file)
index 0000000..b0f9e3e
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __JUMPY_H__
+#define __JUMPY_H__
+
+#include "badguy.hpp"
+
+class Jumpy : public BadGuy
+{
+public:
+  Jumpy(const lisp::Lisp& reader);
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+  void write(lisp::Writer& writer);
+  void active_update(float);
+
+  void freeze();
+  bool is_freezable() const;
+
+  virtual Jumpy* clone() const { return new Jumpy(*this); }
+
+private:
+  HitResponse hit(const CollisionHit& hit);
+  Vector pos_groundhit;
+  bool groundhit_pos_set;
+};
+
+#endif
diff --git a/src/badguy/kugelblitz.cpp b/src/badguy/kugelblitz.cpp
new file mode 100644 (file)
index 0000000..cbfdd99
--- /dev/null
@@ -0,0 +1,216 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "kugelblitz.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "tile.hpp"
+#include "random_generator.hpp"
+
+#define  LIFETIME 5
+#define  MOVETIME 0.75
+#define  BASE_SPEED 200
+#define  RAND_SPEED 150
+
+static const float X_OFFSCREEN_DISTANCE = 1600;
+static const float Y_OFFSCREEN_DISTANCE = 1200;
+
+Kugelblitz::Kugelblitz(const lisp::Lisp& reader)
+    : BadGuy(Vector(0,0), "images/creatures/kugelblitz/kugelblitz.sprite"), groundhit_pos_set(false)
+{
+  reader.get("x", start_position.x);
+  sprite->set_action("falling");
+  physic.enable_gravity(false);
+}
+
+void
+Kugelblitz::write(lisp::Writer& writer)
+{
+  writer.start_list("kugelblitz");
+
+  writer.write_float("x", start_position.x);
+
+  writer.end_list("kugelblitz");
+}
+
+void
+Kugelblitz::activate()
+{
+  physic.set_velocity_y(300);
+  physic.set_velocity_x(-20); //fall a little to the left
+  direction = 1;
+  dying = false;
+}
+
+void
+Kugelblitz::collision_solid(const CollisionHit& chit)
+{
+  hit(chit);
+}
+
+HitResponse
+Kugelblitz::collision_player(Player& player, const CollisionHit& )
+{
+  if(player.is_invincible()) {
+    explode();
+    return ABORT_MOVE;
+  }
+  // hit from above?
+  if(player.get_movement().y - get_movement().y > 0 && player.get_bbox().p2.y <
+      (get_bbox().p1.y + get_bbox().p2.y) / 2) {
+    // if it's not is it possible to squish us, then this will hurt
+    if(!collision_squished(player))
+      player.kill(false);
+      explode();
+    return FORCE_MOVE;
+  }
+  player.kill(false);
+  explode();
+  return FORCE_MOVE;
+}
+
+HitResponse
+Kugelblitz::collision_badguy(BadGuy& other , const CollisionHit& chit)
+{
+  //Let the Kugelblitz explode, too? The problem with that is that
+  //two Kugelblitzes would cancel each other out on contact...
+  other.kill_fall();
+  return hit(chit);
+}
+
+HitResponse
+Kugelblitz::hit(const CollisionHit& hit)
+{
+  // hit floor?
+  if(hit.bottom) {
+    if (!groundhit_pos_set)
+    {
+      pos_groundhit = get_pos();
+      groundhit_pos_set = true;
+    }
+    sprite->set_action("flying");
+    physic.set_velocity_y(0);
+    //Set random initial speed and direction
+    direction = systemRandom.rand(2)? 1: -1;
+    int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
+    physic.set_velocity_x(speed);
+    movement_timer.start(MOVETIME);
+    lifetime.start(LIFETIME);
+
+  } else if(hit.top) { // bumped on roof
+    physic.set_velocity_y(0);
+  }
+
+  return CONTINUE;
+}
+
+void
+Kugelblitz::active_update(float elapsed_time)
+{
+  if (lifetime.check()) {
+    explode();
+  }
+  else {
+    if (groundhit_pos_set) {
+      if (movement_timer.check()) {
+        if (direction == 1) direction = -1; else direction = 1;
+        int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
+        physic.set_velocity_x(speed);
+        movement_timer.start(MOVETIME);
+      }
+    }
+    /*
+    if (Sector::current()->solids->get_tile_at(get_pos())->getAttributes() == 16) {
+      //HIT WATER
+      Sector::current()->add_object(new Electrifier(75,1421,1.5));
+      Sector::current()->add_object(new Electrifier(76,1422,1.5));
+      explode();
+    }
+    if (Sector::current()->solids->get_tile_at(get_pos())->getAttributes() == 48) {
+      //HIT ELECTRIFIED WATER
+      explode();
+    }
+    */
+  }
+  BadGuy::active_update(elapsed_time);
+}
+
+void
+Kugelblitz::kill_fall()
+{
+}
+
+void
+Kugelblitz::explode()
+{
+  if (!dying) {
+    sprite->set_action("pop");
+    lifetime.start(0.2f);
+    dying = true;
+  }
+  else remove_me();
+}
+
+void
+Kugelblitz::try_activate()
+{
+  //FIXME: Don't activate Kugelblitz before it's on-screen
+  float scroll_x = Sector::current()->camera->get_translation().x;
+  float scroll_y = Sector::current()->camera->get_translation().y;
+
+  /* Activate badguys if they're just around the screen to avoid
+   * the effect of having badguys suddenly popping up from nowhere.
+   */
+  if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+      start_position.x < scroll_x - bbox.get_width() &&
+      start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+      start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+    dir = RIGHT;
+    set_state(STATE_ACTIVE);
+    activate();
+  } else if (start_position.x > scroll_x &&
+      start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
+      start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+      start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+    dir = LEFT;
+    set_state(STATE_ACTIVE);
+    activate();
+  } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+      start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
+      ((start_position.y > scroll_y &&
+        start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
+       (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+        start_position.y < scroll_y))) {
+    dir = start_position.x < scroll_x ? RIGHT : LEFT;
+    set_state(STATE_ACTIVE);
+    activate();
+  } else if(state == STATE_INIT
+      && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
+      && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
+      && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
+      && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+    dir = LEFT;
+    set_state(STATE_ACTIVE);
+    activate();
+  }
+}
+
+IMPLEMENT_FACTORY(Kugelblitz, "kugelblitz")
diff --git a/src/badguy/kugelblitz.hpp b/src/badguy/kugelblitz.hpp
new file mode 100644 (file)
index 0000000..5f327f2
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __KUGELBLITZ_H__
+#define __KUGELBLITZ_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+#include "object/electrifier.hpp"
+
+class Kugelblitz : public BadGuy
+{
+public:
+  Kugelblitz(const lisp::Lisp& reader);
+
+  void activate();
+  HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  void write(lisp::Writer& writer);
+  void active_update(float);
+  void kill_fall();
+  void explode();
+
+  virtual Kugelblitz* clone() const { return new Kugelblitz(*this); }
+
+private:
+  void try_activate();
+  HitResponse hit(const CollisionHit& hit);
+  Vector pos_groundhit;
+  bool groundhit_pos_set;
+  bool dying;
+  Timer movement_timer;
+  Timer lifetime;
+  int direction;
+  State state;
+};
+
+#endif
diff --git a/src/badguy/mole.cpp b/src/badguy/mole.cpp
new file mode 100644 (file)
index 0000000..5132743
--- /dev/null
@@ -0,0 +1,168 @@
+//  $Id$
+//
+//  SuperTux - Mole Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "mole.hpp"
+#include "mole_rock.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "random_generator.hpp"
+#include "log.hpp"
+#include "level.hpp"
+
+static const float IDLE_TIME = 0.2f; /**< time to wait before and after throwing */
+static const float THROW_TIME = 4.6f; /**< time to spend throwing */
+static const float THROW_INTERVAL = 1; /**< time between two thrown rocks */
+static const float THROW_VELOCITY = 400; /**< initial velocity of thrown rocks */
+
+Mole::Mole(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/mole/mole.sprite", LAYER_TILES-1), state(PRE_THROWING)
+{
+  physic.enable_gravity(false);
+}
+
+Mole::Mole(const Vector& pos)
+       : BadGuy(pos, "images/creatures/mole/mole.sprite", LAYER_TILES-1), state(PRE_THROWING)
+{
+  physic.enable_gravity(false);
+}
+
+void
+Mole::write(lisp::Writer& writer)
+{
+  writer.start_list("mole");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("mole");
+}
+
+void
+Mole::activate()
+{
+  if (state != DEAD) set_state(PRE_THROWING);
+}
+
+void
+Mole::kill_fall()
+{
+  set_state(DEAD);
+  sound_manager->play("sounds/fall.wav", get_pos());
+  if (countMe) Sector::current()->get_level()->stats.badguys++;
+}
+
+HitResponse
+Mole::collision_badguy(BadGuy& , const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
+
+bool
+Mole::collision_squished(GameObject& )
+{
+  set_state(DEAD);
+  sound_manager->play("sounds/squish.wav", get_pos());
+  if (countMe) Sector::current()->get_level()->stats.badguys++;
+  return true;
+}
+
+void
+Mole::throw_rock()
+{
+  float px = get_bbox().get_middle().x;
+  float py = get_bbox().get_middle().y;
+
+  float angle = systemRandom.rand(90 - 15, 90 + 15) * (M_PI / 180);
+  float vx = cos(angle) * THROW_VELOCITY;
+  float vy = -sin(angle) * THROW_VELOCITY;
+
+  sound_manager->play("sounds/dartfire.wav", get_pos());
+  Sector::current()->add_object(new MoleRock(Vector(px, py), Vector(vx, vy), this));
+}
+
+void
+Mole::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  switch (state) {
+    case PRE_THROWING:
+      if (timer.check()) {
+       set_state(THROWING);
+      }
+      break;
+    case THROWING:
+      if (throw_timer.check()) {
+        throw_rock();
+       throw_timer.start(THROW_INTERVAL);
+      }
+      if (timer.check()) {
+       set_state(POST_THROWING);
+      }
+      break;
+    case POST_THROWING:
+      if (timer.check()) {
+       set_state(PEEKING);
+      }
+      break;
+    case PEEKING:
+      if (sprite->animation_done()) {
+       set_state(PRE_THROWING);
+      }
+      break;
+    case DEAD:
+      break;
+  }
+
+}
+
+void
+Mole::set_state(MoleState new_state)
+{
+  switch (new_state) {
+    case PRE_THROWING:
+      sprite->set_action("idle");
+      set_group(COLGROUP_DISABLED);
+      timer.start(IDLE_TIME);
+      break;
+    case THROWING:
+      sprite->set_action("idle");
+      set_group(COLGROUP_DISABLED);
+      timer.start(THROW_TIME);
+      throw_timer.start(THROW_INTERVAL);
+      break;
+    case POST_THROWING:
+      sprite->set_action("idle");
+      set_group(COLGROUP_DISABLED);
+      timer.start(IDLE_TIME);
+      break;
+    case PEEKING:
+      sprite->set_action("peeking", 1);
+      set_group(COLGROUP_STATIC);
+      break;
+    case DEAD:
+      sprite->set_action("idle");
+      set_group(COLGROUP_DISABLED);
+      break;
+  }
+
+  state = new_state;
+}
+
+IMPLEMENT_FACTORY(Mole, "mole")
diff --git a/src/badguy/mole.hpp b/src/badguy/mole.hpp
new file mode 100644 (file)
index 0000000..2a1fa0f
--- /dev/null
@@ -0,0 +1,59 @@
+//  $Id$
+//
+//  SuperTux - Mole Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MOLE_H__
+#define __MOLE_H__
+
+#include "badguy.hpp"
+
+class Mole : public BadGuy
+{
+public:
+  Mole(const lisp::Lisp& );
+  Mole(const Vector& pos);
+
+  void kill_fall();
+  HitResponse collision_badguy(BadGuy& , const CollisionHit& );
+  bool collision_squished(GameObject& object);
+
+  void activate();
+  void write(lisp::Writer& );
+  void active_update(float);
+
+  virtual Mole* clone() const { return new Mole(*this); }
+
+private:
+  enum MoleState {
+    PRE_THROWING,
+    THROWING,
+    POST_THROWING,
+    PEEKING,
+    DEAD
+  };
+
+  MoleState state;
+  Timer timer;
+  Timer throw_timer;
+
+  void set_state(MoleState new_state);
+  void throw_rock();
+
+};
+
+#endif
diff --git a/src/badguy/mole_rock.cpp b/src/badguy/mole_rock.cpp
new file mode 100644 (file)
index 0000000..c9252ff
--- /dev/null
@@ -0,0 +1,114 @@
+//  $Id$
+//
+//  MoleRock - Rock thrown by "Mole" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+
+#include "mole_rock.hpp"
+
+MoleRock::MoleRock(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/mole/mole_rock.sprite", LAYER_TILES - 2), parent(0), initial_velocity(Vector(0, -400))
+{
+  physic.enable_gravity(true);
+  countMe = false;
+}
+
+MoleRock::MoleRock(const Vector& pos, const Vector& velocity, const BadGuy* parent = 0)
+       : BadGuy(pos, LEFT, "images/creatures/mole/mole_rock.sprite", LAYER_TILES - 2), parent(parent), initial_velocity(velocity)
+{
+  physic.enable_gravity(true);
+  countMe = false;
+}
+
+MoleRock::MoleRock(const MoleRock& other)
+       : BadGuy(other), parent(other.parent), initial_velocity(Vector(0, -400))
+{
+}
+
+MoleRock::~MoleRock()
+{
+}
+
+bool
+MoleRock::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+  if (from_object == parent) {
+    parent = dynamic_cast<MoleRock*>(to_object);
+    return true;
+  }
+  return false;
+}
+
+void
+MoleRock::write(lisp::Writer& writer)
+{
+  writer.start_list("mole_rock");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("mole_rock");
+}
+
+void
+MoleRock::activate()
+{
+  physic.set_velocity(initial_velocity);
+  sprite->set_action("default");
+}
+
+void
+MoleRock::deactivate()
+{
+  remove_me();
+}
+
+void
+MoleRock::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+}
+
+void
+MoleRock::collision_solid(const CollisionHit& )
+{
+  sound_manager->play("sounds/darthit.wav", get_pos());
+  remove_me();
+}
+
+HitResponse
+MoleRock::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+  // ignore collisions with parent
+  if (&badguy == parent) {
+    return FORCE_MOVE;
+  }
+  sound_manager->play("sounds/stomp.wav", get_pos());
+  remove_me();
+  badguy.kill_fall();
+  return ABORT_MOVE;
+}
+
+HitResponse
+MoleRock::collision_player(Player& player, const CollisionHit& hit)
+{
+  sound_manager->play("sounds/stomp.wav", get_pos());
+  remove_me();
+  return BadGuy::collision_player(player, hit);
+}
+
+IMPLEMENT_FACTORY(MoleRock, "mole_rock")
diff --git a/src/badguy/mole_rock.hpp b/src/badguy/mole_rock.hpp
new file mode 100644 (file)
index 0000000..42a7e05
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  MoleRock - Rock thrown by "Mole" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __MOLE_ROCK_H__
+#define __MOLE_ROCK_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "MoleRock" - Rock thrown by "Mole" Badguy
+ */
+class MoleRock : public BadGuy
+{
+public:
+  MoleRock(const lisp::Lisp& reader);
+  MoleRock(const Vector& pos, const Vector& velocity, const BadGuy* parent);
+  MoleRock(const MoleRock& mole_rock);
+  ~MoleRock();
+
+  void activate();
+  void deactivate();
+  void write(lisp::Writer& writer);
+
+  void active_update(float elapsed_time);
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  virtual MoleRock* clone() const { return new MoleRock(*this); }
+
+  virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+  const BadGuy* parent; /**< collisions with this BadGuy will be ignored */
+  const Vector initial_velocity; /**< velocity at time of creation */
+};
+
+#endif
diff --git a/src/badguy/mrbomb.cpp b/src/badguy/mrbomb.cpp
new file mode 100644 (file)
index 0000000..93a58c2
--- /dev/null
@@ -0,0 +1,149 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "mrbomb.hpp"
+#include "bomb.hpp"
+#include "object/explosion.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+
+MrBomb::MrBomb(const lisp::Lisp& reader)
+       : WalkingBadguy(reader, "images/creatures/mr_bomb/mr_bomb.sprite", "left", "right")
+{
+  walk_speed = 80;
+  max_drop_height = 16;
+  grabbed = false;
+
+  //Prevent stutter when Tux jumps on Mr Bomb
+  sound_manager->preload("sounds/explosion.wav");
+
+  //Check if we need another sprite
+  if( !reader.get( "sprite", sprite_name ) ){
+    return;
+  }
+  if( sprite_name == "" ){
+    sprite_name = "images/creatures/mr_bomb/mr_bomb.sprite";
+    return;
+  }
+  //Replace sprite
+  sprite = sprite_manager->create( sprite_name );
+}
+
+/* MrBomb created by a despencer always gets default sprite atm.*/
+MrBomb::MrBomb(const Vector& pos, Direction d)
+       : WalkingBadguy(pos, d, "images/creatures/mr_bomb/mr_bomb.sprite", "left", "right")
+{
+  walk_speed = 80;
+  max_drop_height = 16;
+  grabbed = false;
+  sound_manager->preload("sounds/explosion.wav");
+}
+
+void
+MrBomb::write(lisp::Writer& writer)
+{
+  writer.start_list("mrbomb");
+  WalkingBadguy::write(writer);
+  writer.end_list("mrbomb");
+}
+
+HitResponse
+MrBomb::collision(GameObject& object, const CollisionHit& hit)
+{
+  if(grabbed)
+    return FORCE_MOVE;
+  return WalkingBadguy::collision(object, hit);
+}
+
+HitResponse
+MrBomb::collision_player(Player& player, const CollisionHit& hit)
+{
+  if(grabbed)
+    return FORCE_MOVE;
+  return WalkingBadguy::collision_player(player, hit);
+}
+
+bool
+MrBomb::collision_squished(GameObject& object)
+{
+  remove_me();
+  Sector::current()->add_object(new Bomb(get_pos(), dir, sprite_name ));
+  kill_squished(object);
+  return true;
+}
+
+void
+MrBomb::active_update(float elapsed_time)
+{
+  if(grabbed)
+    return;
+  WalkingBadguy::active_update(elapsed_time);
+}
+
+void
+MrBomb::kill_fall()
+{
+  remove_me();
+  Explosion* explosion = new Explosion(get_bbox().get_middle());
+  Sector::current()->add_object(explosion);
+
+  run_dead_script();
+}
+
+void
+MrBomb::grab(MovingObject&, const Vector& pos, Direction dir)
+{
+  assert(frozen);
+  movement = pos - get_pos();
+  this->dir = dir;
+  sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+  set_group(COLGROUP_DISABLED);
+  grabbed = true;
+}
+
+void
+MrBomb::ungrab(MovingObject& , Direction dir)
+{
+  this->dir = dir;
+  set_group(COLGROUP_MOVING);
+  grabbed = false;
+}
+
+void
+MrBomb::freeze()
+{
+  WalkingBadguy::freeze();
+  sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+}
+
+bool
+MrBomb::is_freezable() const
+{
+  return true;
+}
+
+bool
+MrBomb::is_portable() const
+{
+  return frozen;
+}
+
+IMPLEMENT_FACTORY(MrBomb, "mrbomb")
diff --git a/src/badguy/mrbomb.hpp b/src/badguy/mrbomb.hpp
new file mode 100644 (file)
index 0000000..14f326d
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MRBOMB_H__
+#define __MRBOMB_H__
+
+#include "walking_badguy.hpp"
+#include "object/portable.hpp"
+
+class MrBomb : public WalkingBadguy, public Portable
+{
+public:
+  MrBomb(const lisp::Lisp& reader);
+  MrBomb(const Vector& pos, Direction d);
+
+  void write(lisp::Writer& writer);
+  void kill_fall();
+  HitResponse collision(GameObject& object, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  void active_update(float elapsed_time);
+
+  void grab(MovingObject& object, const Vector& pos, Direction dir);
+  void ungrab(MovingObject& object, Direction dir);
+  bool is_portable() const;
+
+  void freeze();
+  bool is_freezable() const;
+
+  virtual MrBomb* clone() const { return new MrBomb(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+private:
+  bool grabbed;
+};
+
+#endif
diff --git a/src/badguy/mriceblock.cpp b/src/badguy/mriceblock.cpp
new file mode 100644 (file)
index 0000000..cb801b3
--- /dev/null
@@ -0,0 +1,279 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "mriceblock.hpp"
+#include "object/block.hpp"
+
+namespace {
+  const float KICKSPEED = 500;
+  const int MAXSQUISHES = 10;
+}
+
+MrIceBlock::MrIceBlock(const lisp::Lisp& reader)
+  : WalkingBadguy(reader, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_NORMAL), squishcount(0)
+{
+  walk_speed = 80;
+  max_drop_height = 600;
+  sound_manager->preload("sounds/iceblock_bump.wav");
+  sound_manager->preload("sounds/stomp.wav");
+  sound_manager->preload("sounds/kick.wav");
+}
+
+MrIceBlock::MrIceBlock(const Vector& pos, Direction d)
+  : WalkingBadguy(pos, d, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_NORMAL), squishcount(0)
+{
+  walk_speed = 80;
+  max_drop_height = 600;
+  sound_manager->preload("sounds/iceblock_bump.wav");
+  sound_manager->preload("sounds/stomp.wav");
+  sound_manager->preload("sounds/kick.wav");
+}
+
+void
+MrIceBlock::write(lisp::Writer& writer)
+{
+  writer.start_list("mriceblock");
+  WalkingBadguy::write(writer);
+  writer.end_list("mriceblock");
+}
+
+void
+MrIceBlock::activate()
+{
+  WalkingBadguy::activate();
+  set_state(ICESTATE_NORMAL);
+}
+
+void
+MrIceBlock::active_update(float elapsed_time)
+{
+  if(ice_state == ICESTATE_GRABBED)
+    return;
+
+  if(ice_state == ICESTATE_FLAT && flat_timer.check()) {
+    set_state(ICESTATE_NORMAL);
+  }
+
+  if (ice_state == ICESTATE_NORMAL)
+  {
+    WalkingBadguy::active_update(elapsed_time);
+    return;
+  }
+
+  BadGuy::active_update(elapsed_time);
+}
+
+bool
+MrIceBlock::can_break(){
+    return ice_state == ICESTATE_KICKED;
+}
+
+void
+MrIceBlock::collision_solid(const CollisionHit& hit)
+{
+  update_on_ground_flag(hit);
+
+  if(hit.top || hit.bottom) { // floor or roof
+    physic.set_velocity_y(0);
+    return;
+  }
+
+  // hit left or right
+  switch(ice_state) {
+    case ICESTATE_NORMAL:
+      WalkingBadguy::collision_solid(hit);
+      break;
+    case ICESTATE_KICKED: {
+      if(hit.right && dir == RIGHT) {
+        dir = LEFT;
+        sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+        physic.set_velocity_x(-KICKSPEED);
+      } else if(hit.left && dir == LEFT) {
+        dir = RIGHT;
+        sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+        physic.set_velocity_x(KICKSPEED);
+      }
+      sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+      break;
+    }
+    case ICESTATE_FLAT:
+      physic.set_velocity_x(0);
+      break;
+    case ICESTATE_GRABBED:
+      break;
+  }
+}
+
+HitResponse
+MrIceBlock::collision(GameObject& object, const CollisionHit& hit)
+{
+  if(ice_state == ICESTATE_GRABBED)
+    return FORCE_MOVE;
+
+  return BadGuy::collision(object, hit);
+}
+
+HitResponse
+MrIceBlock::collision_player(Player& player, const CollisionHit& hit)
+{
+  if(ice_state == ICESTATE_GRABBED)
+    return FORCE_MOVE;
+
+  if(dir == UP) {
+    return FORCE_MOVE;
+  }
+
+  // handle kicks from left or right side
+  if(ice_state == ICESTATE_FLAT && get_state() == STATE_ACTIVE) {
+    if(hit.left) {
+      dir = RIGHT;
+      player.kick();
+      set_state(ICESTATE_KICKED);
+      return FORCE_MOVE;
+    } else if(hit.right) {
+      dir = LEFT;
+      player.kick();
+      set_state(ICESTATE_KICKED);
+      return FORCE_MOVE;
+    }
+  }
+
+  return BadGuy::collision_player(player, hit);
+}
+
+HitResponse
+MrIceBlock::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+  switch(ice_state) {
+    case ICESTATE_NORMAL:
+      return WalkingBadguy::collision_badguy(badguy, hit);
+    case ICESTATE_FLAT:
+      return FORCE_MOVE;
+    case ICESTATE_KICKED:
+      badguy.kill_fall();
+      return FORCE_MOVE;
+    default:
+      assert(false);
+  }
+
+  return ABORT_MOVE;
+}
+
+bool
+MrIceBlock::collision_squished(GameObject& object)
+{
+  switch(ice_state) {
+    case ICESTATE_KICKED:
+    case ICESTATE_NORMAL:
+      squishcount++;
+      if(squishcount >= MAXSQUISHES) {
+        kill_fall();
+        return true;
+      }
+
+      set_state(ICESTATE_FLAT);
+      break;
+    case ICESTATE_FLAT:
+      {
+       MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
+       if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
+         dir = RIGHT;
+       } else {
+         dir = LEFT;
+       }
+      }
+      set_state(ICESTATE_KICKED);
+      break;
+    case ICESTATE_GRABBED:
+      assert(false);
+      break;
+  }
+
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) player->bounce(*this);
+  return true;
+}
+
+void
+MrIceBlock::set_state(IceState state)
+{
+  if(ice_state == state)
+    return;
+
+  switch(state) {
+    case ICESTATE_NORMAL:
+      WalkingBadguy::activate();
+      break;
+    case ICESTATE_FLAT:
+      if(dir == UP) {
+        physic.set_velocity_y(-KICKSPEED);
+        bbox.set_size(34, 31.8f);
+      } else {
+        sound_manager->play("sounds/stomp.wav", get_pos());
+        physic.set_velocity_x(0);
+        physic.set_velocity_y(0);
+
+        sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+      }
+      flat_timer.start(4);
+      break;
+    case ICESTATE_KICKED:
+      sound_manager->play("sounds/kick.wav", get_pos());
+
+      physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
+      sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+      // we should slide above 1 block holes now...
+      bbox.set_size(34, 31.8f);
+      break;
+    case ICESTATE_GRABBED:
+      flat_timer.stop();
+      break;
+    default:
+      assert(false);
+  }
+  ice_state = state;
+}
+
+void
+MrIceBlock::grab(MovingObject&, const Vector& pos, Direction dir)
+{
+  movement = pos - get_pos();
+  this->dir = dir;
+  sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+  set_state(ICESTATE_GRABBED);
+  set_group(COLGROUP_DISABLED);
+}
+
+void
+MrIceBlock::ungrab(MovingObject& , Direction dir)
+{
+  this->dir = dir;
+  set_state(dir == UP ? ICESTATE_FLAT : ICESTATE_KICKED);
+  set_group(COLGROUP_MOVING);
+}
+
+bool
+MrIceBlock::is_portable() const
+{
+  return ice_state == ICESTATE_FLAT;
+}
+
+IMPLEMENT_FACTORY(MrIceBlock, "mriceblock")
diff --git a/src/badguy/mriceblock.hpp b/src/badguy/mriceblock.hpp
new file mode 100644 (file)
index 0000000..a00c5e7
--- /dev/null
@@ -0,0 +1,67 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MRICEBLOCK_H__
+#define __MRICEBLOCK_H__
+
+#include "walking_badguy.hpp"
+#include "object/portable.hpp"
+
+class MrIceBlock : public WalkingBadguy, public Portable
+{
+public:
+  MrIceBlock(const lisp::Lisp& reader);
+  MrIceBlock(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  HitResponse collision(GameObject& object, const CollisionHit& hit);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+  void active_update(float elapsed_time);
+
+  void grab(MovingObject& object, const Vector& pos, Direction dir);
+  void ungrab(MovingObject& object, Direction dir);
+  bool is_portable() const;
+
+  bool can_break();
+
+  virtual MrIceBlock* clone() const { return new MrIceBlock(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+private:
+  enum IceState {
+    ICESTATE_NORMAL,
+    ICESTATE_FLAT,
+    ICESTATE_GRABBED,
+    ICESTATE_KICKED
+  };
+
+  void set_state(IceState state);
+
+  IceState ice_state;
+  Timer flat_timer;
+  int squishcount;
+};
+
+#endif
diff --git a/src/badguy/mrrocket.cpp b/src/badguy/mrrocket.cpp
new file mode 100644 (file)
index 0000000..4ac7045
--- /dev/null
@@ -0,0 +1,90 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "mrrocket.hpp"
+#include "object/explosion.hpp"
+
+static const float SPEED = 200;
+
+MrRocket::MrRocket(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/mr_rocket/mr_rocket.sprite")
+{
+}
+
+MrRocket::MrRocket(const Vector& pos, Direction d)
+       : BadGuy(pos, d, "images/creatures/mr_rocket/mr_rocket.sprite")
+{
+}
+
+void
+MrRocket::write(lisp::Writer& writer)
+{
+  writer.start_list("mrrocket");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("mrrocket");
+}
+
+void
+MrRocket::activate()
+{
+  physic.set_velocity_x(dir == LEFT ? -SPEED : SPEED);
+  physic.enable_gravity(false);
+  sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+void
+MrRocket::active_update(float elapsed_time)
+{
+  if (collision_timer.check()) {
+    Sector::current()->add_object(new Explosion(get_bbox().get_middle()));
+    remove_me();
+  }
+  else if (!collision_timer.started()) {
+     movement=physic.get_movement(elapsed_time);
+     sprite->set_action(dir == LEFT ? "left" : "right");
+  }
+}
+
+bool
+MrRocket::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  kill_fall();
+  return true;
+}
+
+void
+MrRocket::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    physic.set_velocity_y(0);
+  } else if(hit.left || hit.right) {
+    sprite->set_action(dir == LEFT ? "collision-left" : "collision-right");
+    physic.set_velocity_x(0);
+    collision_timer.start(0.2f, true);
+  }
+}
+
+IMPLEMENT_FACTORY(MrRocket, "mrrocket")
diff --git a/src/badguy/mrrocket.hpp b/src/badguy/mrrocket.hpp
new file mode 100644 (file)
index 0000000..628df37
--- /dev/null
@@ -0,0 +1,44 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MRROCKET_H__
+#define __MRROCKET_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+class MrRocket : public BadGuy
+{
+public:
+  MrRocket(const lisp::Lisp& reader);
+  MrRocket(const Vector& pos, Direction d);
+
+  void activate();
+  void active_update(float elapsed_time);
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+
+  virtual MrRocket* clone() const { return new MrRocket(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+  Timer collision_timer;
+};
+
+#endif
diff --git a/src/badguy/mrtree.cpp b/src/badguy/mrtree.cpp
new file mode 100644 (file)
index 0000000..ddcc43a
--- /dev/null
@@ -0,0 +1,102 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "mrtree.hpp"
+#include "stumpy.hpp"
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+#include "sector.hpp"
+
+static const float WALKSPEED = 100;
+
+static const float POISONIVY_WIDTH = 32;
+static const float POISONIVY_HEIGHT = 32;
+static const float POISONIVY_Y_OFFSET = 24;
+
+
+MrTree::MrTree(const lisp::Lisp& reader)
+  : WalkingBadguy(reader, "images/creatures/mr_tree/mr_tree.sprite","left","right")
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = 16;
+  sound_manager->preload("sounds/mr_tree.ogg");
+}
+
+void
+MrTree::write(lisp::Writer& writer)
+{
+  writer.start_list("mrtree");
+  WalkingBadguy::write(writer);
+  writer.end_list("mrtree");
+}
+
+bool
+MrTree::collision_squished(GameObject& object)
+{
+  // replace with Stumpy
+  Vector stumpy_pos = get_pos();
+  stumpy_pos.x += 20;
+  stumpy_pos.y += 25;
+  Stumpy* stumpy = new Stumpy(stumpy_pos, dir);
+  remove_me();
+  Sector::current()->add_object(stumpy);
+
+  // give Feedback
+  sound_manager->play("sounds/mr_tree.ogg", get_pos());
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) player->bounce(*this);
+
+  // spawn some particles
+  // TODO: provide convenience function in MovingSprite or MovingObject?
+  for (int px = (int)stumpy->get_bbox().p1.x; px < (int)stumpy->get_bbox().p2.x; px+=10) {
+    Vector ppos = Vector(px, stumpy->get_bbox().p1.y-5);
+    float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+    float velocity = systemRandom.randf(45, 90);
+    float vx = sin(angle)*velocity;
+    float vy = -cos(angle)*velocity;
+    Vector pspeed = Vector(vx, vy);
+    Vector paccel = Vector(0, 100);
+    Sector::current()->add_object(new SpriteParticle("images/objects/particles/leaf.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+  }
+
+  // spawn PoisonIvy
+  Vector leaf1_pos = Vector(stumpy_pos.x - POISONIVY_WIDTH - 1, stumpy_pos.y - POISONIVY_Y_OFFSET);
+  Rect leaf1_bbox = Rect(leaf1_pos.x, leaf1_pos.y, leaf1_pos.x + POISONIVY_WIDTH, leaf1_pos.y + POISONIVY_HEIGHT);
+  if (Sector::current()->is_free_of_movingstatics(leaf1_bbox, this)) {
+    PoisonIvy* leaf1 = new PoisonIvy(leaf1_bbox.p1, LEFT);
+    leaf1 = leaf1;
+    Sector::current()->add_object(leaf1);
+  }
+
+  // spawn PoisonIvy
+  Vector leaf2_pos = Vector(stumpy_pos.x + sprite->get_current_hitbox_width() + 1, stumpy_pos.y - POISONIVY_Y_OFFSET);
+  Rect leaf2_bbox = Rect(leaf2_pos.x, leaf2_pos.y, leaf2_pos.x + POISONIVY_WIDTH, leaf2_pos.y + POISONIVY_HEIGHT);
+  if (Sector::current()->is_free_of_movingstatics(leaf2_bbox, this)) {
+    PoisonIvy* leaf2 = new PoisonIvy(leaf2_bbox.p1, RIGHT);
+    leaf2 = leaf2;
+    Sector::current()->add_object(leaf2);
+  }
+
+  return true;
+}
+
+IMPLEMENT_FACTORY(MrTree, "mrtree")
diff --git a/src/badguy/mrtree.hpp b/src/badguy/mrtree.hpp
new file mode 100644 (file)
index 0000000..0b884a1
--- /dev/null
@@ -0,0 +1,37 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MRTREE_H__
+#define __MRTREE_H__
+
+#include "walking_badguy.hpp"
+
+class MrTree : public WalkingBadguy
+{
+public:
+  MrTree(const lisp::Lisp& reader);
+  void write(lisp::Writer& writer);
+  virtual MrTree* clone() const { return new MrTree(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+};
+
+#endif
diff --git a/src/badguy/plant.cpp b/src/badguy/plant.cpp
new file mode 100644 (file)
index 0000000..5830d2b
--- /dev/null
@@ -0,0 +1,117 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "plant.hpp"
+
+static const float WALKSPEED = 80;
+static const float WAKE_TIME = .5;
+
+Plant::Plant(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/plant/plant.sprite")
+{
+  state = PLANT_SLEEPING;
+}
+
+void
+Plant::write(lisp::Writer& writer)
+{
+  writer.start_list("plant");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("plant");
+}
+
+void
+Plant::activate()
+{
+  //FIXME: turns sspiky around for debugging
+  dir = dir == LEFT ? RIGHT : LEFT;
+
+  state = PLANT_SLEEPING;
+  physic.set_velocity_x(0);
+  sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
+}
+
+void
+Plant::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    physic.set_velocity_y(0);
+  } else if(hit.left || hit.right) {
+    dir = dir == LEFT ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(-physic.get_velocity_x());
+  }
+}
+
+HitResponse
+Plant::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+  if(state != PLANT_WALKING) return CONTINUE;
+
+  if(hit.left || hit.right) {
+    dir = dir == LEFT ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(-physic.get_velocity_x());
+  }
+
+  return CONTINUE;
+}
+
+void
+Plant::active_update(float elapsed_time) {
+  BadGuy::active_update(elapsed_time);
+
+  if(state == PLANT_SLEEPING) {
+
+    Player* player = this->get_nearest_player();
+    if (player) {
+      Rect mb = this->get_bbox();
+      Rect pb = player->get_bbox();
+
+      bool inReach_left = (pb.p2.x >= mb.p2.x-((dir == LEFT) ? 256 : 0));
+      bool inReach_right = (pb.p1.x <= mb.p1.x+((dir == RIGHT) ? 256 : 0));
+      bool inReach_top = (pb.p2.y >= mb.p2.y);
+      bool inReach_bottom = (pb.p1.y <= mb.p1.y);
+
+      if (inReach_left && inReach_right && inReach_top && inReach_bottom) {
+        // wake up
+        sprite->set_action(dir == LEFT ? "waking-left" : "waking-right");
+        if(!timer.started()) timer.start(WAKE_TIME);
+        state = PLANT_WAKING;
+      }
+    }
+  }
+
+  if(state == PLANT_WAKING) {
+    if(timer.check()) {
+      // start walking
+      sprite->set_action(dir == LEFT ? "left" : "right");
+      physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+      state = PLANT_WALKING;
+    }
+  }
+
+}
+
+IMPLEMENT_FACTORY(Plant, "plant")
diff --git a/src/badguy/plant.hpp b/src/badguy/plant.hpp
new file mode 100644 (file)
index 0000000..7216514
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PLANT_H__
+#define __PLANT_H__
+
+#include "badguy.hpp"
+
+class Plant : public BadGuy
+{
+public:
+  Plant(const lisp::Lisp& reader);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  void active_update(float elapsed_time);
+
+  virtual Plant* clone() const { return new Plant(*this); }
+
+protected:
+  Timer timer;
+
+  enum PlantState {
+    PLANT_SLEEPING,
+    PLANT_WAKING,
+    PLANT_WALKING
+  };
+  PlantState state;
+
+};
+
+#endif
diff --git a/src/badguy/poisonivy.cpp b/src/badguy/poisonivy.cpp
new file mode 100644 (file)
index 0000000..67b4c7f
--- /dev/null
@@ -0,0 +1,66 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+PoisonIvy::PoisonIvy(const lisp::Lisp& reader)
+       : WalkingBadguy(reader, "images/creatures/poison_ivy/poison_ivy.sprite", "left", "right")
+{
+  walk_speed = 80;
+}
+
+PoisonIvy::PoisonIvy(const Vector& pos, Direction d)
+       : WalkingBadguy(pos, d, "images/creatures/poison_ivy/poison_ivy.sprite", "left", "right")
+{
+  walk_speed = 80;
+}
+
+void
+PoisonIvy::write(lisp::Writer& writer)
+{
+  writer.start_list("poisonivy");
+  WalkingBadguy::write(writer);
+  writer.end_list("poisonivy");
+}
+
+bool
+PoisonIvy::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  // spawn some particles
+  // TODO: provide convenience function in MovingSprite or MovingObject?
+  for (int i = 0; i < 3; i++) {
+    Vector ppos = bbox.get_middle();
+    float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+    float velocity = systemRandom.randf(350, 400);
+    float vx = sin(angle)*velocity;
+    float vy = -cos(angle)*velocity;
+    Vector pspeed = Vector(vx, vy);
+    Vector paccel = Vector(0, 100);
+    Sector::current()->add_object(new SpriteParticle("images/objects/particles/poisonivy.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+  }
+  kill_squished(object);
+  return true;
+}
+
+IMPLEMENT_FACTORY(PoisonIvy, "poisonivy")
diff --git a/src/badguy/poisonivy.hpp b/src/badguy/poisonivy.hpp
new file mode 100644 (file)
index 0000000..783f232
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __POISONIVY_H__
+#define __POISONIVY_H__
+
+#include "walking_badguy.hpp"
+
+class PoisonIvy : public WalkingBadguy
+{
+public:
+  PoisonIvy(const lisp::Lisp& reader);
+  PoisonIvy(const Vector& pos, Direction d);
+
+  void write(lisp::Writer& writer);
+  virtual PoisonIvy* clone() const { return new PoisonIvy(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+};
+
+#endif
diff --git a/src/badguy/root.cpp b/src/badguy/root.cpp
new file mode 100644 (file)
index 0000000..cad33cb
--- /dev/null
@@ -0,0 +1,98 @@
+//  $Id$
+//
+//  SuperTux - "Will-O-Wisp" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "root.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "timer.hpp"
+
+static const float SPEED_GROW = 256;
+static const float SPEED_SHRINK = 128;
+static const float HATCH_TIME = 0.75;
+
+Root::Root(const Vector& pos)
+  : BadGuy(pos, "images/creatures/ghosttree/root.sprite", LAYER_TILES-1),
+    mystate(STATE_APPEARING), offset_y(0)
+{
+  base_sprite.reset(sprite_manager->create("images/creatures/ghosttree/root-base.sprite"));
+  base_sprite->set_action("appearing", 1);
+  base_sprite->set_animation_loops(1); // TODO: necessary because set_action ignores loops for default action
+  physic.enable_gravity(false);
+}
+
+Root::~Root()
+{
+}
+
+void
+Root::activate()
+{
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+Root::deactivate()
+{
+  remove_me(); 
+}
+
+void
+Root::active_update(float elapsed_time)
+{
+  if (mystate == STATE_APPEARING) {  
+    if (base_sprite->animation_done()) {
+      hatch_timer.start(HATCH_TIME);
+      mystate = STATE_HATCHING;
+    }
+  }
+  if (mystate == STATE_HATCHING) {
+    if (!hatch_timer.started()) mystate = STATE_GROWING;
+  }
+  else if (mystate == STATE_GROWING) {
+    offset_y -= elapsed_time * SPEED_GROW;
+    if (offset_y < -sprite->get_height()) {
+      offset_y = -sprite->get_height();
+      mystate = STATE_SHRINKING;
+    }
+    set_pos(start_position + Vector(0, offset_y));
+  }
+  else if (mystate == STATE_SHRINKING) {
+    offset_y += elapsed_time * SPEED_SHRINK;
+    if (offset_y > 0) {
+      offset_y = 0;
+      mystate = STATE_VANISHING;
+      base_sprite->set_action("vanishing", 2);
+      base_sprite->set_animation_loops(2); // TODO: doesn't seem to work for loops=1 
+    }
+    set_pos(start_position + Vector(0, offset_y));
+  }
+  else if (mystate == STATE_VANISHING) {
+    if (base_sprite->animation_done()) remove_me();
+  }
+  BadGuy::active_update(elapsed_time);
+}
+
+void
+Root::draw(DrawingContext& context)
+{
+  base_sprite->draw(context, start_position, LAYER_TILES+1);
+  if ((mystate != STATE_APPEARING) && (mystate != STATE_VANISHING)) BadGuy::draw(context);
+}
+
diff --git a/src/badguy/root.hpp b/src/badguy/root.hpp
new file mode 100644 (file)
index 0000000..d693f23
--- /dev/null
@@ -0,0 +1,51 @@
+//  $Id$
+//
+//  SuperTux - Boss "GhostTree"
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __ROOT_H__
+#define __ROOT_H__
+
+#include <memory>
+#include "badguy.hpp"
+
+class Timer;
+
+class Root : public BadGuy
+{
+public:
+  Root(const Vector& pos);
+  ~Root();
+
+  void activate();
+  void deactivate();
+  void active_update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+  virtual bool is_flammable() const { return false; }
+  virtual bool is_freezable() const { return false; }
+  virtual void kill_fall() { }
+
+protected:
+  enum MyState {
+    STATE_APPEARING, STATE_HATCHING, STATE_GROWING, STATE_SHRINKING, STATE_VANISHING
+  };
+  MyState mystate;
+  std::auto_ptr<Sprite> base_sprite;
+  float offset_y;
+  Timer hatch_timer;
+};
+
+#endif
diff --git a/src/badguy/skullyhop.cpp b/src/badguy/skullyhop.cpp
new file mode 100644 (file)
index 0000000..2b086ea
--- /dev/null
@@ -0,0 +1,150 @@
+//  $Id$
+//
+//  SkullyHop - A Hopping Skull
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "skullyhop.hpp"
+#include "random_generator.hpp"
+
+namespace {
+  const float VERTICAL_SPEED = -450;   /**< y-speed when jumping */
+  const float HORIZONTAL_SPEED = 220; /**< x-speed when jumping */
+  const float MIN_RECOVER_TIME = 0.1f; /**< minimum time to stand still before starting a (new) jump */
+  const float MAX_RECOVER_TIME = 1.0f; /**< maximum time to stand still before starting a (new) jump */
+  static const std::string HOP_SOUND = "sounds/hop.ogg";
+}
+
+SkullyHop::SkullyHop(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/skullyhop/skullyhop.sprite")
+{
+  sound_manager->preload( HOP_SOUND );
+}
+
+SkullyHop::SkullyHop(const Vector& pos, Direction d)
+       : BadGuy(pos, d, "images/creatures/skullyhop/skullyhop.sprite")
+{
+  sound_manager->preload( HOP_SOUND );
+}
+
+void
+SkullyHop::write(lisp::Writer& writer)
+{
+  writer.start_list("skullyhop");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("skullyhop");
+}
+
+void
+SkullyHop::activate()
+{
+  // initial state is JUMPING, because we might start airborne
+  state = JUMPING;
+  sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+}
+
+void
+SkullyHop::set_state(SkullyHopState newState)
+{
+  if (newState == STANDING) {
+    physic.set_velocity_x(0);
+    physic.set_velocity_y(0);
+    sprite->set_action(dir == LEFT ? "standing-left" : "standing-right");
+
+    float recover_time = systemRandom.randf(MIN_RECOVER_TIME,MAX_RECOVER_TIME);
+    recover_timer.start(recover_time);
+  } else
+  if (newState == CHARGING) {
+    sprite->set_action(dir == LEFT ? "charging-left" : "charging-right", 1);
+  } else
+  if (newState == JUMPING) {
+    sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+    physic.set_velocity_x(dir == LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
+    physic.set_velocity_y(VERTICAL_SPEED);
+    sound_manager->play( HOP_SOUND, get_pos());
+  }
+
+  state = newState;
+}
+
+bool
+SkullyHop::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+void
+SkullyHop::collision_solid(const CollisionHit& hit)
+{
+  // just default behaviour (i.e. stop at floor/walls) when squished
+  if (BadGuy::get_state() == STATE_SQUISHED) {
+    BadGuy::collision_solid(hit);
+  }
+
+  // ignore collisions while standing still
+  if(state != JUMPING)
+    return;
+
+  // check if we hit the floor while falling
+  if(hit.bottom && physic.get_velocity_y() > 0 ) {
+    set_state(STANDING);
+  }
+  // check if we hit the roof while climbing
+  if(hit.top) {
+    physic.set_velocity_y(0);
+  }
+
+  // check if we hit left or right while moving in either direction
+  if(hit.left || hit.right) {
+    dir = dir == LEFT ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+    physic.set_velocity_x(-0.25*physic.get_velocity_x());
+  }
+}
+
+HitResponse
+SkullyHop::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+  // behaviour for badguy collisions is the same as for collisions with solids
+  collision_solid(hit);
+
+  return CONTINUE;
+}
+
+void
+SkullyHop::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  // charge when fully recovered
+  if ((state == STANDING) && (recover_timer.check())) {
+    set_state(CHARGING);
+    return;
+  }
+
+  // jump as soon as charging animation completed
+  if ((state == CHARGING) && (sprite->animation_done())) {
+    set_state(JUMPING);
+    return;
+  }
+}
+
+IMPLEMENT_FACTORY(SkullyHop, "skullyhop")
diff --git a/src/badguy/skullyhop.hpp b/src/badguy/skullyhop.hpp
new file mode 100644 (file)
index 0000000..0569722
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SkullyHop - A Hopping Skull
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __SKULLYHOP_H__
+#define __SKULLYHOP_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "SkullyHop" - A Hopping Skull
+ */
+class SkullyHop : public BadGuy
+{
+public:
+  SkullyHop(const lisp::Lisp& reader);
+  SkullyHop(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  bool collision_squished(GameObject& object);
+  void active_update(float elapsed_time);
+
+  virtual SkullyHop* clone() const { return new SkullyHop(*this); }
+
+protected:
+  enum SkullyHopState {
+    STANDING,
+    CHARGING,
+    JUMPING
+  };
+
+  Timer recover_timer;
+  SkullyHopState state;
+
+  void set_state(SkullyHopState newState);
+};
+
+#endif
diff --git a/src/badguy/snail.cpp b/src/badguy/snail.cpp
new file mode 100644 (file)
index 0000000..a269533
--- /dev/null
@@ -0,0 +1,248 @@
+//  $Id$
+//
+//  SuperTux - Badguy "Snail"
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "snail.hpp"
+#include "object/block.hpp"
+
+namespace {
+  const float KICKSPEED = 500;
+  const int MAXSQUISHES = 10;
+  const float KICKSPEED_Y = -500; /**< y-velocity gained when kicked */
+}
+
+Snail::Snail(const lisp::Lisp& reader)
+  : WalkingBadguy(reader, "images/creatures/snail/snail.sprite", "left", "right"), state(STATE_NORMAL), squishcount(0)
+{
+  walk_speed = 80;
+  max_drop_height = 600;
+  sound_manager->preload("sounds/iceblock_bump.wav");
+  sound_manager->preload("sounds/stomp.wav");
+  sound_manager->preload("sounds/kick.wav");
+}
+
+Snail::Snail(const Vector& pos, Direction d)
+  : WalkingBadguy(pos, d, "images/creatures/snail/snail.sprite", "left", "right"), state(STATE_NORMAL), squishcount(0)
+{
+  walk_speed = 80;
+  max_drop_height = 600;
+  sound_manager->preload("sounds/iceblock_bump.wav");
+  sound_manager->preload("sounds/stomp.wav");
+  sound_manager->preload("sounds/kick.wav");
+}
+
+void
+Snail::write(lisp::Writer& writer)
+{
+  writer.start_list("snail");
+  WalkingBadguy::write(writer);
+  writer.end_list("snail");
+}
+
+void
+Snail::activate()
+{
+  WalkingBadguy::activate();
+  be_normal();
+}
+
+void
+Snail::be_normal()
+{
+  if (state == STATE_NORMAL) return;
+
+  state = STATE_NORMAL;
+  WalkingBadguy::activate();
+}
+
+void
+Snail::be_flat()
+{
+  state = STATE_FLAT;
+  sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+  sprite->set_fps(64);
+
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+
+  flat_timer.start(4);
+}
+
+void
+Snail::be_kicked()
+{
+  state = STATE_KICKED_DELAY;
+  sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+  sprite->set_fps(64);
+
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+
+  // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
+  kicked_delay_timer.start(0.05f);
+}
+
+bool
+Snail::can_break(){
+    return state == STATE_KICKED;
+}
+
+void
+Snail::active_update(float elapsed_time)
+{
+  switch (state) {
+
+    case STATE_NORMAL:
+      WalkingBadguy::active_update(elapsed_time);
+      break;
+
+    case STATE_FLAT:
+      if (flat_timer.started()) {
+       sprite->set_fps(64 - 15 * flat_timer.get_timegone());
+      }
+      if (flat_timer.check()) {
+       be_normal();
+      }
+      BadGuy::active_update(elapsed_time);
+      break;
+
+    case STATE_KICKED_DELAY:
+      if (kicked_delay_timer.check()) {
+       physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
+       physic.set_velocity_y(KICKSPEED_Y);
+       state = STATE_KICKED;
+      }
+      BadGuy::active_update(elapsed_time);
+      break;
+
+    case STATE_KICKED:
+      physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
+      if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
+      BadGuy::active_update(elapsed_time);
+      break;
+
+  }
+}
+
+void
+Snail::collision_solid(const CollisionHit& hit)
+{
+  update_on_ground_flag(hit);
+
+  switch (state) {
+    case STATE_NORMAL:
+      WalkingBadguy::collision_solid(hit);
+      break;
+    case STATE_FLAT:
+      if(hit.top || hit.bottom) {
+       physic.set_velocity_y(0);
+      }
+      if(hit.left || hit.right) {
+      }
+      break;
+    case STATE_KICKED_DELAY:
+      if(hit.top || hit.bottom) {
+       physic.set_velocity_y(0);
+      }
+      if(hit.left || hit.right) {
+       physic.set_velocity_x(0);
+      }
+      break;
+    case STATE_KICKED:
+      if(hit.top || hit.bottom) {
+       physic.set_velocity_y(0);
+      }
+      if(hit.left || hit.right) {
+       sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+
+       if( ( dir == LEFT && hit.left ) || ( dir == RIGHT && hit.right) ){
+         dir = (dir == LEFT) ? RIGHT : LEFT;
+         sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+
+         physic.set_velocity_x(-physic.get_velocity_x()*0.75);
+         if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
+       }
+
+      }
+      break;
+  }
+
+}
+
+HitResponse
+Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+  switch(state) {
+    case STATE_NORMAL:
+      return WalkingBadguy::collision_badguy(badguy, hit);
+    case STATE_FLAT:
+    case STATE_KICKED_DELAY:
+      return FORCE_MOVE;
+    case STATE_KICKED:
+      badguy.kill_fall();
+      return FORCE_MOVE;
+    default:
+      assert(false);
+  }
+
+  return ABORT_MOVE;
+}
+
+bool
+Snail::collision_squished(GameObject& object)
+{
+  switch(state) {
+
+    case STATE_KICKED:
+    case STATE_NORMAL:
+      squishcount++;
+      if(squishcount >= MAXSQUISHES) {
+        kill_fall();
+        return true;
+      }
+
+      sound_manager->play("sounds/stomp.wav", get_pos());
+      be_flat();
+      break;
+
+    case STATE_FLAT:
+      sound_manager->play("sounds/kick.wav", get_pos());
+      {
+       MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
+       if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
+         dir = RIGHT;
+       } else {
+         dir = LEFT;
+       }
+      }
+      be_kicked();
+      break;
+
+    case STATE_KICKED_DELAY:
+      break;
+
+  }
+
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) player->bounce(*this);
+  return true;
+}
+
+IMPLEMENT_FACTORY(Snail, "snail")
diff --git a/src/badguy/snail.hpp b/src/badguy/snail.hpp
new file mode 100644 (file)
index 0000000..642df55
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  SuperTux - Badguy "Snail"
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SNAIL_H__
+#define __SNAIL_H__
+
+#include "walking_badguy.hpp"
+
+/**
+ * Badguy "Snail" - a snail-like creature that can be flipped and tossed around at an angle
+ */
+class Snail : public WalkingBadguy
+{
+public:
+  Snail(const lisp::Lisp& reader);
+  Snail(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  bool can_break();
+
+  void active_update(float elapsed_time);
+
+  virtual Snail* clone() const { return new Snail(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+  void be_normal(); /**< switch to state STATE_NORMAL */
+  void be_flat(); /**< switch to state STATE_FLAT */
+  void be_kicked(); /**< switch to state STATE_KICKED_DELAY */
+
+private:
+  enum State {
+    STATE_NORMAL, /**< walking around */
+    STATE_FLAT, /**< flipped upside-down */
+    STATE_KICKED_DELAY, /**< short delay before being launched */
+    STATE_KICKED /**< launched */
+  };
+  State state;
+  Timer flat_timer; /**< wait time until flipping right-side-up again */
+  Timer kicked_delay_timer; /**< wait time until switching from STATE_KICKED_DELAY to STATE_KICKED */
+  int squishcount;
+};
+
+#endif
diff --git a/src/badguy/snowball.cpp b/src/badguy/snowball.cpp
new file mode 100644 (file)
index 0000000..8e690cb
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "snowball.hpp"
+
+SnowBall::SnowBall(const lisp::Lisp& reader)
+    : WalkingBadguy(reader, "images/creatures/snowball/snowball.sprite", "left", "right")
+{
+  walk_speed = 80;
+}
+
+SnowBall::SnowBall(const Vector& pos, Direction d)
+    : WalkingBadguy(pos, d, "images/creatures/snowball/snowball.sprite", "left", "right")
+{
+  walk_speed = 80;
+}
+
+void
+SnowBall::write(lisp::Writer& writer)
+{
+  writer.start_list("snowball");
+  WalkingBadguy::write(writer);
+  writer.end_list("snowball");
+}
+
+bool
+SnowBall::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+IMPLEMENT_FACTORY(SnowBall, "snowball")
diff --git a/src/badguy/snowball.hpp b/src/badguy/snowball.hpp
new file mode 100644 (file)
index 0000000..ef32818
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SNOWBALL_H__
+#define __SNOWBALL_H__
+
+#include "walking_badguy.hpp"
+
+class SnowBall : public WalkingBadguy
+{
+public:
+  SnowBall(const lisp::Lisp& reader);
+  SnowBall(const Vector& pos, Direction d);
+
+  void write(lisp::Writer& writer);
+  virtual SnowBall* clone() const { return new SnowBall(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+};
+
+#endif
diff --git a/src/badguy/spidermite.cpp b/src/badguy/spidermite.cpp
new file mode 100644 (file)
index 0000000..96a90a0
--- /dev/null
@@ -0,0 +1,98 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <stdio.h>
+
+#include "spidermite.hpp"
+
+static const float FLYTIME = 1.2f;
+static const float FLYSPEED = -100.0f;
+
+SpiderMite::SpiderMite(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/spidermite/spidermite.sprite")
+{
+  physic.enable_gravity(false);
+}
+
+SpiderMite::SpiderMite(const Vector& pos)
+       : BadGuy(pos, "images/creatures/spidermite/spidermite.sprite")
+{
+  physic.enable_gravity(false);
+}
+
+void
+SpiderMite::write(lisp::Writer& writer)
+{
+  writer.start_list("spidermite");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("spidermite");
+}
+
+void
+SpiderMite::activate()
+{
+  sprite->set_action(dir == LEFT ? "left" : "right");
+  mode = FLY_UP;
+  physic.set_velocity_y(FLYSPEED);
+  timer.start(FLYTIME/2);
+}
+
+bool
+SpiderMite::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+void
+SpiderMite::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) { // hit floor or roof?
+    physic.set_velocity_y(0);
+  }
+}
+
+void
+SpiderMite::active_update(float elapsed_time)
+{
+  if(timer.check()) {
+    if(mode == FLY_UP) {
+      mode = FLY_DOWN;
+      physic.set_velocity_y(-FLYSPEED);
+    } else if(mode == FLY_DOWN) {
+      mode = FLY_UP;
+      physic.set_velocity_y(FLYSPEED);
+    }
+    timer.start(FLYTIME);
+  }
+  movement=physic.get_movement(elapsed_time);
+
+  Player* player = this->get_nearest_player();
+  if (player) {
+    dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+  }
+}
+
+IMPLEMENT_FACTORY(SpiderMite, "spidermite")
diff --git a/src/badguy/spidermite.hpp b/src/badguy/spidermite.hpp
new file mode 100644 (file)
index 0000000..ee36b5b
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SPIDERMITE_H__
+#define __SPIDERMITE_H__
+
+#include "badguy.hpp"
+
+class SpiderMite : public BadGuy
+{
+public:
+  SpiderMite(const lisp::Lisp& reader);
+  SpiderMite(const Vector& pos);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void active_update(float elapsed_time);
+  void collision_solid(const CollisionHit& hit);
+
+  virtual SpiderMite* clone() const { return new SpiderMite(*this); }
+
+protected:
+  enum SpiderMiteMode {
+    FLY_UP,
+    FLY_DOWN
+  };
+  SpiderMiteMode mode;
+  bool collision_squished(GameObject& object);
+private:
+  Timer timer;
+};
+
+#endif
diff --git a/src/badguy/spiky.cpp b/src/badguy/spiky.cpp
new file mode 100644 (file)
index 0000000..eebf19a
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "spiky.hpp"
+
+Spiky::Spiky(const lisp::Lisp& reader)
+       : WalkingBadguy(reader, "images/creatures/spiky/spiky.sprite", "left", "right")
+{
+  walk_speed = 80;
+  max_drop_height = 600;
+}
+
+void
+Spiky::write(lisp::Writer& writer)
+{
+  writer.start_list("spiky");
+  WalkingBadguy::write(writer);
+  writer.end_list("spiky");
+}
+
+void
+Spiky::freeze()
+{
+  WalkingBadguy::freeze();
+  sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+}
+
+bool
+Spiky::is_freezable() const
+{
+  return true;
+}
+
+IMPLEMENT_FACTORY(Spiky, "spiky")
diff --git a/src/badguy/spiky.hpp b/src/badguy/spiky.hpp
new file mode 100644 (file)
index 0000000..b90a0ec
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SPIKY_H__
+#define __SPIKY_H__
+
+#include "walking_badguy.hpp"
+
+class Spiky : public WalkingBadguy
+{
+public:
+  Spiky(const lisp::Lisp& reader);
+
+  void write(lisp::Writer& writer);
+  virtual Spiky* clone() const { return new Spiky(*this); }
+
+  void freeze();
+  bool is_freezable() const;
+
+private:
+};
+
+#endif
diff --git a/src/badguy/sspiky.cpp b/src/badguy/sspiky.cpp
new file mode 100644 (file)
index 0000000..ad80b8d
--- /dev/null
@@ -0,0 +1,123 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "sspiky.hpp"
+
+static const float WALKSPEED = 80;
+
+SSpiky::SSpiky(const lisp::Lisp& reader)
+       : WalkingBadguy(reader, "images/creatures/spiky/sleepingspiky.sprite", "left", "right"), state(SSPIKY_SLEEPING)
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = -1;
+}
+
+void
+SSpiky::write(lisp::Writer& writer)
+{
+  writer.start_list("sspiky");
+  WalkingBadguy::write(writer);
+  writer.end_list("sspiky");
+}
+
+void
+SSpiky::activate()
+{
+  state = SSPIKY_SLEEPING;
+  physic.set_velocity_x(0);
+  sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
+}
+
+void
+SSpiky::collision_solid(const CollisionHit& hit)
+{
+  if(state != SSPIKY_WALKING) {
+    BadGuy::collision_solid(hit);
+    return;
+  }
+  WalkingBadguy::collision_solid(hit);
+}
+
+HitResponse
+SSpiky::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+  if(state != SSPIKY_WALKING) {
+    return BadGuy::collision_badguy(badguy, hit);
+  }
+  return WalkingBadguy::collision_badguy(badguy, hit);
+}
+
+void
+SSpiky::active_update(float elapsed_time) {
+
+  if(state == SSPIKY_WALKING) {
+    WalkingBadguy::active_update(elapsed_time);
+    return;
+  }
+
+  if(state == SSPIKY_SLEEPING) {
+
+    Player* player = this->get_nearest_player();
+    if (player) {
+      Rect mb = this->get_bbox();
+      Rect pb = player->get_bbox();
+
+      bool inReach_left = (pb.p2.x >= mb.p2.x-((dir == LEFT) ? 256 : 0));
+      bool inReach_right = (pb.p1.x <= mb.p1.x+((dir == RIGHT) ? 256 : 0));
+      bool inReach_top = (pb.p2.y >= mb.p1.y);
+      bool inReach_bottom = (pb.p1.y <= mb.p2.y);
+
+      if (inReach_left && inReach_right && inReach_top && inReach_bottom) {
+        // wake up
+        sprite->set_action(dir == LEFT ? "waking-left" : "waking-right", 1);
+        state = SSPIKY_WAKING;
+      }
+    }
+
+    BadGuy::active_update(elapsed_time);
+  }
+
+  if(state == SSPIKY_WAKING) {
+    if(sprite->animation_done()) {
+      // start walking
+      state = SSPIKY_WALKING;
+      WalkingBadguy::activate();
+    }
+
+    BadGuy::active_update(elapsed_time);
+  }
+}
+
+void
+SSpiky::freeze()
+{
+  WalkingBadguy::freeze();
+  sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+  state = SSPIKY_WALKING; // if we get hit while sleeping, wake up :)
+}
+
+bool
+SSpiky::is_freezable() const
+{
+  return true;
+}
+
+IMPLEMENT_FACTORY(SSpiky, "sspiky")
diff --git a/src/badguy/sspiky.hpp b/src/badguy/sspiky.hpp
new file mode 100644 (file)
index 0000000..8e346cd
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SSPIKY_H__
+#define __SSPIKY_H__
+
+#include "walking_badguy.hpp"
+
+class SSpiky : public WalkingBadguy
+{
+public:
+  SSpiky(const lisp::Lisp& reader);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  void active_update(float elapsed_time);
+
+  void freeze();
+  bool is_freezable() const;
+
+  virtual SSpiky* clone() const { return new SSpiky(*this); }
+
+protected:
+  enum SSpikyState {
+    SSPIKY_SLEEPING,
+    SSPIKY_WAKING,
+    SSPIKY_WALKING
+  };
+  SSpikyState state;
+};
+
+#endif
diff --git a/src/badguy/stalactite.cpp b/src/badguy/stalactite.cpp
new file mode 100644 (file)
index 0000000..8bde2fb
--- /dev/null
@@ -0,0 +1,153 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "stalactite.hpp"
+#include "random_generator.hpp"
+
+static const int SHAKE_RANGE_X = 40;
+static const float SHAKE_TIME = .8f;
+static const float SQUISH_TIME = 2;
+static const float SHAKE_RANGE_Y = 400;
+
+Stalactite::Stalactite(const lisp::Lisp& lisp)
+       : BadGuy(lisp, "images/creatures/stalactite/stalactite.sprite", LAYER_TILES - 1), state(STALACTITE_HANGING)
+{
+  countMe = false;
+}
+
+void
+Stalactite::write(lisp::Writer& writer)
+{
+  writer.start_list("stalactite");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("stalactite");
+}
+
+void
+Stalactite::active_update(float elapsed_time)
+{
+  if(state == STALACTITE_HANGING) {
+    Player* player = this->get_nearest_player();
+    if (player) {
+      if(player->get_bbox().p2.x > bbox.p1.x - SHAKE_RANGE_X
+          && player->get_bbox().p1.x < bbox.p2.x + SHAKE_RANGE_X
+          && player->get_bbox().p2.y > bbox.p1.y
+         && player->get_bbox().p1.y < bbox.p2.y + SHAKE_RANGE_Y) {
+        timer.start(SHAKE_TIME);
+        state = STALACTITE_SHAKING;
+      }
+    }
+  } else if(state == STALACTITE_SHAKING) {
+    if(timer.check()) {
+      state = STALACTITE_FALLING;
+      physic.enable_gravity(true);
+    }
+  } else if(state == STALACTITE_FALLING || state == STALACTITE_SQUISHED) {
+    movement = physic.get_movement(elapsed_time);
+    if(state == STALACTITE_SQUISHED && timer.check())
+      remove_me();
+  }
+}
+
+void
+Stalactite::squish()
+{
+  state = STALACTITE_SQUISHED;
+  set_group(COLGROUP_MOVING_ONLY_STATIC);
+  sprite->set_action("squished");
+  if(!timer.started())
+    timer.start(SQUISH_TIME);
+}
+
+void
+Stalactite::collision_solid(const CollisionHit& hit)
+{
+  if(state == STALACTITE_FALLING) {
+    if (hit.bottom) squish();
+  }
+  if(state == STALACTITE_SQUISHED) {
+    physic.set_velocity_y(0);
+  }
+}
+
+HitResponse
+Stalactite::collision_player(Player& player)
+{
+  if(state != STALACTITE_SQUISHED) {
+    player.kill(false);
+  }
+
+  return FORCE_MOVE;
+}
+
+HitResponse
+Stalactite::collision_badguy(BadGuy& other, const CollisionHit& hit)
+{
+  if (state == STALACTITE_SQUISHED) return FORCE_MOVE;
+  if (state != STALACTITE_FALLING) return BadGuy::collision_badguy(other, hit);
+
+  // ignore other Stalactites
+  if (dynamic_cast<Stalactite*>(&other)) return FORCE_MOVE;
+
+  if (other.is_freezable()) {
+    other.freeze();
+  } else {
+    other.kill_fall();
+  }
+
+  remove_me();
+
+  return FORCE_MOVE;
+}
+
+void
+Stalactite::kill_fall()
+{
+}
+
+void
+Stalactite::draw(DrawingContext& context)
+{
+  if(get_state() != STATE_ACTIVE)
+    return;
+
+
+  if(state == STALACTITE_SQUISHED) {
+    sprite->draw(context, get_pos(), LAYER_OBJECTS);
+    return;
+  }
+
+  if(state == STALACTITE_SHAKING) {
+    sprite->draw(context, get_pos() + Vector(systemRandom.rand(-3,3), 0), layer);
+  } else {
+    sprite->draw(context, get_pos(), layer);
+  }
+}
+
+void
+Stalactite::deactivate()
+{
+  if(state != STALACTITE_HANGING)
+    remove_me();
+}
+
+IMPLEMENT_FACTORY(Stalactite, "stalactite")
diff --git a/src/badguy/stalactite.hpp b/src/badguy/stalactite.hpp
new file mode 100644 (file)
index 0000000..bf9484b
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __STALACTITE_H__
+#define __STALACTITE_H__
+
+#include "badguy.hpp"
+
+class Stalactite : public BadGuy
+{
+public:
+  Stalactite(const lisp::Lisp& reader);
+
+  void active_update(float elapsed_time);
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_player(Player& player);
+  HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+  void kill_fall();
+  void draw(DrawingContext& context);
+  void deactivate();
+
+  virtual Stalactite* clone() const { return new Stalactite(*this); }
+
+  void squish();
+
+protected:
+  Timer timer;
+
+  enum StalactiteState {
+    STALACTITE_HANGING,
+    STALACTITE_SHAKING,
+    STALACTITE_FALLING,
+    STALACTITE_SQUISHED
+  };
+  StalactiteState state;
+};
+
+#endif
diff --git a/src/badguy/stumpy.cpp b/src/badguy/stumpy.cpp
new file mode 100644 (file)
index 0000000..c53fe39
--- /dev/null
@@ -0,0 +1,167 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "stumpy.hpp"
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+static const float WALKSPEED = 120;
+static const float INVINCIBLE_TIME = 1;
+
+Stumpy::Stumpy(const lisp::Lisp& reader)
+  : WalkingBadguy(reader, "images/creatures/mr_tree/stumpy.sprite","left","right"), mystate(STATE_NORMAL)
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = 16;
+  sound_manager->preload("sounds/mr_tree.ogg");
+  sound_manager->preload("sounds/mr_treehit.ogg");
+}
+
+Stumpy::Stumpy(const Vector& pos, Direction d)
+  : WalkingBadguy(pos, d, "images/creatures/mr_tree/stumpy.sprite","left","right"), mystate(STATE_INVINCIBLE)
+{
+  walk_speed = WALKSPEED;
+  max_drop_height = 16;
+  sound_manager->preload("sounds/mr_treehit.ogg");
+  invincible_timer.start(INVINCIBLE_TIME);
+}
+
+
+void
+Stumpy::write(lisp::Writer& writer)
+{
+  writer.start_list("stumpy");
+  WalkingBadguy::write(writer);
+  writer.end_list("stumpy");
+}
+
+void
+Stumpy::activate()
+{
+  switch (mystate) {
+    case STATE_INVINCIBLE:
+      sprite->set_action(dir == LEFT ? "dizzy-left" : "dizzy-right");
+      bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+      physic.set_velocity_x(0);
+      break;
+    case STATE_NORMAL:
+      WalkingBadguy::activate();
+      break;
+  }
+}
+
+void
+Stumpy::active_update(float elapsed_time)
+{
+  switch (mystate) {
+    case STATE_INVINCIBLE:
+      if (invincible_timer.check()) {
+       mystate = STATE_NORMAL;
+        WalkingBadguy::activate();
+      }
+      BadGuy::active_update(elapsed_time);
+      break;
+    case STATE_NORMAL:
+      WalkingBadguy::active_update(elapsed_time);
+      break;
+  }
+}
+
+bool
+Stumpy::collision_squished(GameObject& object)
+{
+
+  // if we're still invincible, we ignore the hit
+  if (mystate == STATE_INVINCIBLE) {
+    sound_manager->play("sounds/mr_treehit.ogg", get_pos());
+    Player* player = dynamic_cast<Player*>(&object);
+    if (player) player->bounce(*this);
+    return true;
+  }
+
+  // if we can die, we do
+  if (mystate == STATE_NORMAL) {
+    sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+    set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+    kill_squished(object);
+    // spawn some particles
+    // TODO: provide convenience function in MovingSprite or MovingObject?
+    for (int i = 0; i < 25; i++) {
+      Vector ppos = bbox.get_middle();
+      float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+      float velocity = systemRandom.randf(45, 90);
+      float vx = sin(angle)*velocity;
+      float vy = -cos(angle)*velocity;
+      Vector pspeed = Vector(vx, vy);
+      Vector paccel = Vector(0, 100);
+      Sector::current()->add_object(new SpriteParticle("images/objects/particles/bark.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+    }
+
+    return true;
+
+  }
+
+  //TODO: exception?
+  return true;
+}
+
+void
+Stumpy::collision_solid(const CollisionHit& hit)
+{
+  update_on_ground_flag(hit);
+
+  switch (mystate) {
+    case STATE_INVINCIBLE:
+      if(hit.top || hit.bottom) {
+        physic.set_velocity_y(0);
+      }
+      if(hit.left || hit.right) {
+        physic.set_velocity_x(0);
+      }
+      break;
+    case STATE_NORMAL:
+      WalkingBadguy::collision_solid(hit);
+      break;
+  }
+}
+
+HitResponse
+Stumpy::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+  switch (mystate) {
+    case STATE_INVINCIBLE:
+      if(hit.top || hit.bottom) {
+       physic.set_velocity_y(0);
+      }
+      if(hit.left || hit.right) {
+       physic.set_velocity_x(0);
+      }
+      return CONTINUE;
+      break;
+    case STATE_NORMAL:
+      return WalkingBadguy::collision_badguy(badguy, hit);
+      break;
+  }
+  return CONTINUE;
+}
+
+IMPLEMENT_FACTORY(Stumpy, "stumpy")
diff --git a/src/badguy/stumpy.hpp b/src/badguy/stumpy.hpp
new file mode 100644 (file)
index 0000000..80117fe
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __STUMPY_H__
+#define __STUMPY_H__
+
+#include "walking_badguy.hpp"
+
+class Stumpy : public WalkingBadguy
+{
+public:
+  Stumpy(const lisp::Lisp& reader);
+  Stumpy(const Vector& pos, Direction d);
+
+  void activate();
+  void active_update(float elapsed_time);
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+  virtual Stumpy* clone() const { return new Stumpy(*this); }
+
+protected:
+  enum MyState {
+    STATE_INVINCIBLE, STATE_NORMAL
+  };
+  MyState mystate;
+
+  Timer invincible_timer;
+
+  bool collision_squished(GameObject& object);
+};
+
+#endif
diff --git a/src/badguy/toad.cpp b/src/badguy/toad.cpp
new file mode 100644 (file)
index 0000000..26da7e3
--- /dev/null
@@ -0,0 +1,164 @@
+//  $Id$
+//
+//  Toad - A jumping toad
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "toad.hpp"
+#include "random_generator.hpp"
+
+namespace {
+  const float VERTICAL_SPEED = -450;   /**< y-speed when jumping */
+  const float HORIZONTAL_SPEED = 320; /**< x-speed when jumping */
+  const float RECOVER_TIME = 0.5; /**< time to stand still before starting a (new) jump */
+  static const std::string HOP_SOUND = "sounds/hop.ogg";
+}
+
+Toad::Toad(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/toad/toad.sprite")
+{
+  sound_manager->preload(HOP_SOUND);
+}
+
+Toad::Toad(const Vector& pos, Direction d)
+       : BadGuy(pos, d, "images/creatures/toad/toad.sprite")
+{
+  sound_manager->preload(HOP_SOUND);
+}
+
+void
+Toad::write(lisp::Writer& writer)
+{
+  writer.start_list("toad");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("toad");
+}
+
+void
+Toad::activate()
+{
+  // initial state is JUMPING, because we might start airborne
+  state = JUMPING;
+  sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+}
+
+void
+Toad::set_state(ToadState newState)
+{
+  if (newState == IDLE) {
+    physic.set_velocity_x(0);
+    physic.set_velocity_y(0);
+    sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+
+    recover_timer.start(RECOVER_TIME);
+  } else
+  if (newState == JUMPING) {
+    sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+    physic.set_velocity_x(dir == LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
+    physic.set_velocity_y(VERTICAL_SPEED);
+    sound_manager->play( HOP_SOUND, get_pos());
+  } else
+  if (newState == FALLING) {
+    Player* player = get_nearest_player();
+    // face player
+    if (player && (player->get_bbox().p2.x < get_bbox().p1.x) && (dir == RIGHT)) dir = LEFT;
+    if (player && (player->get_bbox().p1.x > get_bbox().p2.x) && (dir == LEFT)) dir = RIGHT;
+    sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+  }
+
+  state = newState;
+}
+
+bool
+Toad::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+void
+Toad::collision_solid(const CollisionHit& hit)
+{
+  // just default behaviour (i.e. stop at floor/walls) when squished
+  if (BadGuy::get_state() == STATE_SQUISHED) {
+    BadGuy::collision_solid(hit);
+    return;
+  }
+
+  // ignore collisions while standing still
+  if(state == IDLE) {
+    return;
+  }
+
+  // check if we hit left or right while moving in either direction
+  if(((physic.get_velocity_x() < 0) && hit.left) || ((physic.get_velocity_x() > 0) && hit.right)) {
+    /*
+    dir = dir == LEFT ? RIGHT : LEFT;
+    if (state == JUMPING) {
+      sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+    } else {
+      sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+    }
+    */
+    physic.set_velocity_x(-0.25*physic.get_velocity_x());
+  }
+
+  // check if we hit the floor while falling
+  if ((state == FALLING) && hit.bottom) {
+    set_state(IDLE);
+    return;
+  }
+
+  // check if we hit the roof while climbing
+  if ((state == JUMPING) && hit.top) {
+    physic.set_velocity_y(0);
+  }
+
+}
+
+HitResponse
+Toad::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+  // behaviour for badguy collisions is the same as for collisions with solids
+  collision_solid(hit);
+
+  return CONTINUE;
+}
+
+void
+Toad::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  // change sprite when we are falling
+  if ((state == JUMPING) && (physic.get_velocity_y() > 0)) {
+    set_state(FALLING);
+    return;
+  }
+
+  // jump when fully recovered
+  if ((state == IDLE) && (recover_timer.check())) {
+    set_state(JUMPING);
+    return;
+  }
+
+}
+
+IMPLEMENT_FACTORY(Toad, "toad")
diff --git a/src/badguy/toad.hpp b/src/badguy/toad.hpp
new file mode 100644 (file)
index 0000000..8067162
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  Toad - A jumping toad
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __TOAD_H__
+#define __TOAD_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "Toad" - A jumping toad
+ */
+class Toad : public BadGuy
+{
+public:
+  Toad(const lisp::Lisp& reader);
+  Toad(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  bool collision_squished(GameObject& object);
+  void active_update(float elapsed_time);
+
+  virtual Toad* clone() const { return new Toad(*this); }
+
+protected:
+  enum ToadState {
+    IDLE,
+    JUMPING,
+    FALLING
+  };
+
+  Timer recover_timer;
+  ToadState state;
+
+  void set_state(ToadState newState);
+};
+
+#endif
diff --git a/src/badguy/totem.cpp b/src/badguy/totem.cpp
new file mode 100644 (file)
index 0000000..139aa14
--- /dev/null
@@ -0,0 +1,283 @@
+//  $Id$
+//
+//  SuperTux - "Totem" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+
+#include "totem.hpp"
+#include "log.hpp"
+
+static const float WALKSPEED = 100;
+static const float JUMP_ON_SPEED_Y = -400;
+static const float JUMP_OFF_SPEED_Y = -500;
+static const std::string LAND_ON_TOTEM_SOUND = "sounds/totem.ogg";
+
+Totem::Totem(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/totem/totem.sprite")
+{
+  carrying = 0;
+  carried_by = 0;
+  sound_manager->preload( LAND_ON_TOTEM_SOUND );
+}
+
+Totem::Totem(const Totem& other)
+       : BadGuy(other), carrying(other.carrying), carried_by(other.carried_by)
+{
+  sound_manager->preload( LAND_ON_TOTEM_SOUND );
+}
+
+Totem::~Totem()
+{
+  if (carrying) carrying->jump_off();
+  if (carried_by) jump_off();
+}
+
+bool
+Totem::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+  if (from_object == carrying) {
+    carrying = dynamic_cast<Totem*>(to_object);
+    return true;
+  }
+  if (from_object == carried_by) {
+    carried_by = dynamic_cast<Totem*>(to_object);
+    return true;
+  }
+  return false;
+}
+
+void
+Totem::write(lisp::Writer& writer)
+{
+  writer.start_list("totem");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("totem");
+}
+
+void
+Totem::activate()
+{
+  if (!carried_by) {
+    physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+    sprite->set_action(dir == LEFT ? "walking-left" : "walking-right");
+    return;
+  } else {
+    synchronize_with(carried_by);
+    sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
+    return;
+  }
+}
+
+void
+Totem::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  if (!carried_by) {
+    if (on_ground() && might_fall())
+    {
+      dir = (dir == LEFT ? RIGHT : LEFT);
+      activate();
+    }
+
+    Sector* s = Sector::current();
+    if (s) {
+      // jump a bit if we find a suitable totem
+      for (std::vector<MovingObject*>::iterator i = s->moving_objects.begin(); i != s->moving_objects.end(); i++) {
+       Totem* t = dynamic_cast<Totem*>(*i);
+       if (!t) continue;
+
+       // skip if we are not approaching each other
+       if (!((this->dir == LEFT) && (t->dir == RIGHT))) continue;
+
+       Vector p1 = this->get_pos();
+       Vector p2 = t->get_pos();
+
+       // skip if not on same height
+       float dy = (p1.y - p2.y);
+       if (fabsf(dy - 0) > 2) continue;
+
+       // skip if too far away
+       float dx = (p1.x - p2.x);
+       if (fabsf(dx - 128) > 2) continue;
+
+       physic.set_velocity_y(JUMP_ON_SPEED_Y);
+       p1.y -= 1;
+       this->set_pos(p1);
+       break;
+      }
+    }
+  }
+
+  if (carried_by) {
+    this->synchronize_with(carried_by);
+  }
+
+  if (carrying) {
+    carrying->synchronize_with(this);
+  }
+
+}
+
+bool
+Totem::collision_squished(GameObject& object)
+{
+  if (carrying) carrying->jump_off();
+  if (carried_by) {
+    Player* player = dynamic_cast<Player*>(&object);
+    if (player) player->bounce(*this);
+    jump_off();
+  }
+
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+  kill_squished(object);
+  return true;
+}
+
+void
+Totem::collision_solid(const CollisionHit& hit)
+{
+  update_on_ground_flag(hit);
+
+  // if we are being carried around, pass event to bottom of stack and ignore it
+  if (carried_by) {
+    carried_by->collision_solid(hit);
+    return;
+  }
+
+  // If we hit something from above or below: stop moving in this direction
+  if (hit.top || hit.bottom) {
+    physic.set_velocity_y(0);
+  }
+
+  // If we are hit from the direction we are facing: turn around
+  if (hit.left && (dir == LEFT)) {
+    dir = RIGHT;
+    activate();
+  }
+  if (hit.right && (dir == RIGHT)) {
+    dir = LEFT;
+    activate();
+  }
+}
+
+HitResponse
+Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+  // if we are being carried around, pass event to bottom of stack and ignore it
+  if (carried_by) {
+    carried_by->collision_badguy(badguy, hit);
+    return CONTINUE;
+  }
+
+  // if we hit a Totem that is not from our stack: have our base jump on its top
+  Totem* totem = dynamic_cast<Totem*>(&badguy);
+  if (totem) {
+    Totem* thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by;
+    Totem* srcBase = totem; while (srcBase->carried_by)  srcBase=srcBase->carried_by;
+    Totem* thisTop = this;  while (thisTop->carrying)    thisTop=thisTop->carrying;
+    if (srcBase != thisBase) {
+      srcBase->jump_on(thisTop);
+    }
+  }
+
+  // If we are hit from the direction we are facing: turn around
+  if(hit.left && (dir == LEFT)) {
+    dir = RIGHT;
+    activate();
+  }
+  if(hit.right && (dir == RIGHT)) {
+    dir = LEFT;
+    activate();
+  }
+
+  return CONTINUE;
+}
+
+void
+Totem::kill_fall()
+{
+  if (carrying) carrying->jump_off();
+  if (carried_by) jump_off();
+
+  BadGuy::kill_fall();
+}
+
+void
+Totem::jump_on(Totem* target)
+{
+  if (target->carrying) {
+    log_warning << "target is already carrying someone" << std::endl;
+    return;
+  }
+
+  target->carrying = this;
+
+  this->carried_by = target;
+  this->activate();
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+  sound_manager->play( LAND_ON_TOTEM_SOUND , get_pos());
+
+
+  this->synchronize_with(target);
+}
+
+void
+Totem::jump_off() {
+  if (!carried_by) {
+    log_warning << "not carried by anyone" << std::endl;
+    return;
+  }
+
+  carried_by->carrying = 0;
+
+  this->carried_by = 0;
+
+  this->activate();
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+
+  physic.set_velocity_y(JUMP_OFF_SPEED_Y);
+}
+
+void
+Totem::synchronize_with(Totem* base)
+{
+
+  if (dir != base->dir) {
+    dir = base->dir;
+    sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
+  }
+
+  Vector pos = base->get_pos();
+  pos.y -= sprite->get_current_hitbox_height();
+  set_pos(pos);
+
+  physic.set_velocity_x(base->physic.get_velocity_x());
+  physic.set_velocity_y(base->physic.get_velocity_y());
+}
+
+
+IMPLEMENT_FACTORY(Totem, "totem")
diff --git a/src/badguy/totem.hpp b/src/badguy/totem.hpp
new file mode 100644 (file)
index 0000000..682fe45
--- /dev/null
@@ -0,0 +1,58 @@
+//  $Id$
+//
+//  SuperTux - "Totem" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __TOTEM_H__
+#define __TOTEM_H__
+
+#include "badguy.hpp"
+
+/**
+ * "Totem" Badguy - A variable-height stack of wooden blocks
+ */
+class Totem : public BadGuy
+{
+public:
+  Totem(const lisp::Lisp& reader);
+  Totem(const Totem& totem);
+  ~Totem();
+
+  void activate();
+  void active_update(float elapsed_time);
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+  virtual Totem* clone() const { return new Totem(*this); }
+  virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+  Totem* carrying; /**< Totem we are currently carrying (or 0) */
+  Totem* carried_by; /**< Totem by which we are currently carried (or 0) */
+
+  bool collision_squished(GameObject& object);
+  void kill_fall();
+
+  void jump_on(Totem* target); /**< jump on target */
+  void jump_off(); /**< jump off current base */
+
+  void synchronize_with(Totem* baseTotem); /**< synchronize position and movement with baseTotem */
+};
+
+#endif
diff --git a/src/badguy/treewillowisp.cpp b/src/badguy/treewillowisp.cpp
new file mode 100644 (file)
index 0000000..e1eb027
--- /dev/null
@@ -0,0 +1,157 @@
+//  $Id$
+//
+//  SuperTux - "Will-O-Wisp" Badguy
+//  Copyright (C) 2007 Matthias Braun
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "treewillowisp.hpp"
+#include "ghosttree.hpp"
+#include "object/lantern.hpp"
+
+static const std::string SOUNDFILE = "sounds/willowisp.wav";
+static const float       SUCKSPEED = 25;
+
+TreeWillOWisp::TreeWillOWisp(GhostTree* tree, const Vector& pos,
+                             float radius, float speed)
+  : BadGuy(Vector(0, 0), "images/creatures/willowisp/willowisp.sprite",
+           LAYER_OBJECTS - 20), was_sucked(false), mystate(STATE_DEFAULT), tree(tree)
+{
+  treepos_delta = pos;
+  sound_manager->preload(SOUNDFILE);
+
+  this->radius = radius;
+  this->angle  = 0;
+  this->speed  = speed;
+  start_position = tree->get_pos() + treepos_delta;
+}
+
+TreeWillOWisp::~TreeWillOWisp()
+{
+}
+
+void
+TreeWillOWisp::activate()
+{
+  sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+  sound_source->set_position(get_pos());
+  sound_source->set_looping(true);
+  sound_source->set_gain(2.0);
+  sound_source->set_reference_distance(32);
+  sound_source->play();
+
+  set_group(COLGROUP_MOVING);
+}
+
+void
+TreeWillOWisp::vanish()
+{
+  mystate = STATE_VANISHING;
+  sprite->set_action("vanishing", 1);
+  set_group(COLGROUP_DISABLED);
+}
+
+void
+TreeWillOWisp::start_sucking(Vector suck_target)
+{
+  mystate = STATE_SUCKED;
+  this->suck_target = suck_target;
+  was_sucked = true;
+}
+
+HitResponse
+TreeWillOWisp::collision_player(Player& player, const CollisionHit& hit)
+{
+  //TODO: basically a no-op. Remove if this doesn't change.
+  return BadGuy::collision_player(player, hit);
+}
+
+bool
+TreeWillOWisp::collides(GameObject& other, const CollisionHit& ) {
+  Lantern* lantern = dynamic_cast<Lantern*>(&other);
+  if (lantern && lantern->is_open())
+    return true;
+  if (dynamic_cast<Player*>(&other))
+    return true;
+  
+  return false;
+}
+
+void
+TreeWillOWisp::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), layer);
+
+  context.push_target();
+  context.set_target(DrawingContext::LIGHTMAP);
+
+  sprite->draw(context, get_pos(), layer);
+
+  context.pop_target();
+}
+
+void
+TreeWillOWisp::active_update(float elapsed_time)
+{
+  // remove TreeWillOWisp if it has completely vanished
+  if (mystate == STATE_VANISHING) {
+    if(sprite->animation_done()) {
+      remove_me();
+      tree->willowisp_died(this);
+    }
+    return;
+  }
+
+  if (mystate == STATE_SUCKED) {
+    Vector dir = suck_target - get_pos();
+    if(dir.norm() < 5) {
+      vanish();
+      return;
+    }
+    Vector newpos = get_pos() + dir * elapsed_time;
+    movement = newpos - get_pos();
+    return;
+  }
+
+  angle = fmodf(angle + elapsed_time * speed, (float) (2*M_PI));
+  Vector newpos(tree->get_pos() + treepos_delta + Vector(sin(angle) * radius, 0));
+  movement = newpos - get_pos();
+  float sizemod = cos(angle) * 0.8f;
+  /* TODO: modify sprite size */
+
+  sound_source->set_position(get_pos());
+
+  if(sizemod < 0) {
+    layer = LAYER_OBJECTS + 5;
+  } else {
+    layer = LAYER_OBJECTS - 20;
+  }
+}
+
+void
+TreeWillOWisp::set_color(const Color& color)
+{
+  this->color = color;
+  sprite->set_color(color);
+}
+
+Color
+TreeWillOWisp::get_color() const
+{
+  return color;
+}
+
diff --git a/src/badguy/treewillowisp.hpp b/src/badguy/treewillowisp.hpp
new file mode 100644 (file)
index 0000000..c95cbc8
--- /dev/null
@@ -0,0 +1,76 @@
+//  $Id$
+//
+//  SuperTux - "Will-O-Wisp" Badguy
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __TREEWILLOWISP_H__
+#define __TREEWILLOWISP_H__
+
+#include "badguy.hpp"
+
+class GhostTree;
+
+class TreeWillOWisp : public BadGuy
+{
+public:
+  TreeWillOWisp(GhostTree* tree, const Vector& pos, float radius, float speed);
+  virtual ~TreeWillOWisp();
+
+  void activate();
+
+  /**
+   * make TreeWillOWisp vanish
+   */
+  void vanish();
+  void start_sucking(Vector suck_target);
+  bool was_sucked;
+
+  void active_update(float elapsed_time);
+  void set_color(const Color& color);
+  Color get_color() const;
+
+  virtual bool is_flammable() const { return false; }
+  virtual bool is_freezable() const { return false; }
+  virtual void kill_fall() { vanish(); }
+
+  virtual void draw(DrawingContext& context);
+
+protected:
+  virtual bool collides(GameObject& other, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+private:
+  enum MyState {
+    STATE_DEFAULT, STATE_VANISHING, STATE_SUCKED
+  };
+  MyState mystate;
+
+  Color color;
+  float angle;
+  float radius;
+  float speed;
+
+  std::auto_ptr<SoundSource> sound_source;
+  Vector     treepos_delta;
+  GhostTree* tree;
+
+  Vector suck_target;
+};
+
+#endif
+
diff --git a/src/badguy/walking_badguy.cpp b/src/badguy/walking_badguy.cpp
new file mode 100644 (file)
index 0000000..3b8f7ab
--- /dev/null
@@ -0,0 +1,146 @@
+//  $Id$
+//
+//  SuperTux - WalkingBadguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "walking_badguy.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+WalkingBadguy::WalkingBadguy(const Vector& pos, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+       : BadGuy(pos, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+WalkingBadguy::WalkingBadguy(const Vector& pos, Direction direction, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+       : BadGuy(pos, direction, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+WalkingBadguy::WalkingBadguy(const lisp::Lisp& reader, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+       : BadGuy(reader, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+void
+WalkingBadguy::write(lisp::Writer& writer)
+{
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+}
+
+void
+WalkingBadguy::activate()
+{
+  if(frozen)
+    return;
+  sprite->set_action(dir == LEFT ? walk_left_action : walk_right_action);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  physic.set_velocity_x(dir == LEFT ? -walk_speed : walk_speed);
+}
+
+void
+WalkingBadguy::active_update(float elapsed_time)
+{
+  BadGuy::active_update(elapsed_time);
+
+  if (max_drop_height > -1) {
+    if (on_ground() && might_fall(max_drop_height+1))
+    {
+      turn_around();
+    }
+  }
+
+}
+
+void
+WalkingBadguy::collision_solid(const CollisionHit& hit)
+{
+
+  update_on_ground_flag(hit);
+
+  if (hit.top) {
+    if (physic.get_velocity_y() < 0) physic.set_velocity_y(0);
+  }
+  if (hit.bottom) {
+    if (physic.get_velocity_y() > 0) physic.set_velocity_y(0);
+  }
+
+  if ((hit.left && (hit.slope_normal.y == 0) && (dir == LEFT)) || (hit.right && (hit.slope_normal.y == 0) && (dir == RIGHT))) {
+    turn_around();
+  }
+
+}
+
+HitResponse
+WalkingBadguy::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+
+  if ((hit.left && (dir == LEFT)) || (hit.right && (dir == RIGHT))) {
+    turn_around();
+  }
+
+  return CONTINUE;
+}
+
+void
+WalkingBadguy::turn_around()
+{
+  if(frozen)
+    return;
+  dir = dir == LEFT ? RIGHT : LEFT;
+  sprite->set_action(dir == LEFT ? walk_left_action : walk_right_action);
+  physic.set_velocity_x(-physic.get_velocity_x());
+
+  // if we get dizzy, we fall off the screen
+  if (turn_around_timer.started()) {
+    if (turn_around_counter++ > 10) kill_fall();
+  } else {
+    turn_around_timer.start(1);
+    turn_around_counter = 0;
+  }
+
+}
+
+void
+WalkingBadguy::freeze()
+{
+  BadGuy::freeze();
+  physic.set_velocity_x(0);
+}
+
+void
+WalkingBadguy::unfreeze()
+{
+  BadGuy::unfreeze();
+  WalkingBadguy::activate();
+}
+
+
+float
+WalkingBadguy::get_velocity_y() const
+{
+  return physic.get_velocity_y();
+}
+
+void
+WalkingBadguy::set_velocity_y(float vy)
+{
+  physic.set_velocity_y(vy);
+}
diff --git a/src/badguy/walking_badguy.hpp b/src/badguy/walking_badguy.hpp
new file mode 100644 (file)
index 0000000..5d85039
--- /dev/null
@@ -0,0 +1,59 @@
+//  $Id$
+//
+//  SuperTux - WalkingBadguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __WALKING_BADGUY_H__
+#define __WALKING_BADGUY_H__
+
+#include "badguy.hpp"
+
+class Timer;
+
+/**
+ * Baseclass for a Badguy that just walks around.
+ */
+class WalkingBadguy : public BadGuy
+{
+public:
+  WalkingBadguy(const Vector& pos, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+  WalkingBadguy(const Vector& pos, Direction direction, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+  WalkingBadguy(const lisp::Lisp& reader, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void active_update(float elapsed_time);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+  void freeze();
+  void unfreeze();
+
+  float get_velocity_y() const;
+  void set_velocity_y(float vy);
+
+protected:
+  void turn_around();
+
+  std::string walk_left_action;
+  std::string walk_right_action;
+  float walk_speed;
+  int max_drop_height; /**< Maximum height of drop before we will turn around, or -1 to just drop from any ledge */
+  Timer turn_around_timer;
+  int turn_around_counter; /**< counts number of turns since turn_around_timer was started */
+};
+
+#endif
diff --git a/src/badguy/walkingleaf.cpp b/src/badguy/walkingleaf.cpp
new file mode 100644 (file)
index 0000000..a057441
--- /dev/null
@@ -0,0 +1,48 @@
+//  $Id$
+//
+//  SuperTux - Walking Leaf
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "walkingleaf.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+WalkingLeaf::WalkingLeaf(const lisp::Lisp& reader)
+       : WalkingBadguy(reader, "images/creatures/walkingleaf/walkingleaf.sprite", "left", "right")
+{
+  walk_speed = 60;
+  max_drop_height = 16;
+}
+
+WalkingLeaf::WalkingLeaf(const Vector& pos, Direction d)
+       : WalkingBadguy(pos, d, "images/creatures/walkingleaf/walkingleaf.sprite", "left", "right")
+{
+  walk_speed = 60;
+  max_drop_height = 16;
+}
+
+bool
+WalkingLeaf::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  return true;
+}
+
+IMPLEMENT_FACTORY(WalkingLeaf, "walkingleaf")
diff --git a/src/badguy/walkingleaf.hpp b/src/badguy/walkingleaf.hpp
new file mode 100644 (file)
index 0000000..e2b84b1
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux - Walking Leaf
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __WALKINGLEAF_H__
+#define __WALKINGLEAF_H__
+
+#include "walking_badguy.hpp"
+
+/*
+ * Easy to kill badguy that does not jump down from it's ledge.
+ */
+class WalkingLeaf : public WalkingBadguy
+{
+public:
+  WalkingLeaf(const lisp::Lisp& reader);
+  WalkingLeaf(const Vector& pos, Direction d);
+
+  virtual WalkingLeaf* clone() const { return new WalkingLeaf(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+
+};
+
+#endif
diff --git a/src/badguy/willowisp.cpp b/src/badguy/willowisp.cpp
new file mode 100644 (file)
index 0000000..cc50d6b
--- /dev/null
@@ -0,0 +1,279 @@
+//  $Id$
+//
+//  SuperTux - "Will-O-Wisp" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "willowisp.hpp"
+#include "log.hpp"
+#include "game_session.hpp"
+#include "object/lantern.hpp"
+#include "object/player.hpp"
+#include "scripting/squirrel_util.hpp"
+
+static const float FLYSPEED = 64; /**< speed in px per second */
+static const float TRACK_RANGE = 384; /**< at what distance to start tracking the player */
+static const float VANISH_RANGE = 512; /**< at what distance to stop tracking and vanish */
+static const std::string SOUNDFILE = "sounds/willowisp.wav";
+
+WillOWisp::WillOWisp(const lisp::Lisp& reader)
+  : BadGuy(reader, "images/creatures/willowisp/willowisp.sprite", LAYER_FLOATINGOBJECTS), mystate(STATE_IDLE), target_sector("main"), target_spawnpoint("main")
+{
+  bool running = false;
+  flyspeed     = FLYSPEED;
+  track_range  = TRACK_RANGE;
+  vanish_range = VANISH_RANGE;
+
+  reader.get("sector", target_sector);
+  reader.get("spawnpoint", target_spawnpoint);
+  reader.get("name", name);
+  reader.get("flyspeed", flyspeed);
+  reader.get("track-range", track_range);
+  reader.get("vanish-range", vanish_range);
+  reader.get("hit-script", hit_script);
+  reader.get("running", running);
+
+  const lisp::Lisp* pathLisp = reader.get_lisp("path");
+  if(pathLisp != NULL) {
+    path.reset(new Path());
+    path->read(*pathLisp);
+    walker.reset(new PathWalker(path.get(), running));
+    if(running)
+      mystate = STATE_PATHMOVING_TRACK;
+  }
+
+  countMe = false;
+  sound_manager->preload(SOUNDFILE);
+}
+
+void
+WillOWisp::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), layer);
+
+  context.push_target();
+  context.set_target(DrawingContext::LIGHTMAP);
+
+  sprite->draw(context, get_pos(), layer);
+
+  context.pop_target();
+}
+
+void
+WillOWisp::active_update(float elapsed_time)
+{
+  Player* player = get_nearest_player();
+  if (!player) return;
+  Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
+  Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
+  Vector dist = (p2 - p1);
+
+  switch(mystate) {
+  case STATE_STOPPED:
+    break;
+
+  case STATE_IDLE:
+    if (dist.norm() <= track_range) {
+      mystate = STATE_TRACKING;
+    }
+    break;
+
+  case STATE_TRACKING:
+    if (dist.norm() <= vanish_range) {
+      Vector dir = dist.unit();
+      movement = dir * elapsed_time * flyspeed;
+    } else {
+      vanish();
+    }
+    sound_source->set_position(get_pos());
+    break;
+
+  case STATE_WARPING:
+    if(sprite->animation_done()) {
+      remove_me();
+    }
+
+  case STATE_VANISHING: {
+    Vector dir = dist.unit();
+    movement = dir * elapsed_time * flyspeed;
+    if(sprite->animation_done()) {
+      remove_me();
+    }
+    break;
+  }
+
+  case STATE_PATHMOVING:
+  case STATE_PATHMOVING_TRACK:
+    if(walker.get() == NULL)
+      return;
+    movement = walker->advance(elapsed_time) - get_pos();
+    if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
+      mystate = STATE_TRACKING;
+    }
+    break;
+
+  default:
+    assert(false);
+  }
+}
+
+void
+WillOWisp::activate()
+{
+  sprite->set_action("idle");
+
+  sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+  sound_source->set_position(get_pos());
+  sound_source->set_looping(true);
+  sound_source->set_gain(2.0);
+  sound_source->set_reference_distance(32);
+  sound_source->play();
+}
+
+void
+WillOWisp::deactivate()
+{
+  sound_source.reset(NULL);
+
+  switch (mystate) {
+    case STATE_STOPPED:
+    case STATE_IDLE:
+    case STATE_PATHMOVING:
+    case STATE_PATHMOVING_TRACK:
+      break;
+    case STATE_TRACKING:
+      mystate = STATE_IDLE;
+      break;
+    case STATE_WARPING:
+    case STATE_VANISHING:
+      remove_me();
+      break;
+  }
+}
+
+void
+WillOWisp::vanish()
+{
+  mystate = STATE_VANISHING;
+  sprite->set_action("vanishing", 1);
+  set_group(COLGROUP_DISABLED);
+}
+
+bool
+WillOWisp::collides(GameObject& other, const CollisionHit& ) {
+  Lantern* lantern = dynamic_cast<Lantern*>(&other);
+
+  if (lantern && lantern->is_open())
+    return true;
+
+  if (dynamic_cast<Player*>(&other))
+    return true;
+
+  return false;
+}
+
+HitResponse
+WillOWisp::collision_player(Player& player, const CollisionHit& ) {
+  if(player.is_invincible())
+    return ABORT_MOVE;
+
+  if (mystate != STATE_TRACKING)
+    return ABORT_MOVE;
+
+  mystate = STATE_WARPING;
+  sprite->set_action("warping", 1);
+
+  if(hit_script != "") {
+    std::istringstream stream(hit_script);
+    Sector::current()->run_script(stream, "hit-script");
+  } else {
+    GameSession::current()->respawn(target_sector, target_spawnpoint);
+  }
+  sound_manager->play("sounds/warp.wav");
+
+  return CONTINUE;
+}
+
+void
+WillOWisp::goto_node(int node_no)
+{
+  walker->goto_node(node_no);
+  if(mystate != STATE_PATHMOVING && mystate != STATE_PATHMOVING_TRACK) {
+    mystate = STATE_PATHMOVING;
+  }
+}
+
+void
+WillOWisp::start_moving()
+{
+  walker->start_moving();
+}
+
+void
+WillOWisp::stop_moving()
+{
+  walker->stop_moving();
+}
+
+void
+WillOWisp::set_state(const std::string& new_state)
+{
+  if(new_state == "stopped") {
+    mystate = STATE_STOPPED;
+  } else if(new_state == "idle") {
+    mystate = STATE_IDLE;
+  } else if(new_state == "move_path") {
+    mystate = STATE_PATHMOVING;
+    walker->start_moving();
+  } else if(new_state == "move_path_track") {
+    mystate = STATE_PATHMOVING_TRACK;
+    walker->start_moving();
+  } else if(new_state == "normal") {
+    mystate = STATE_IDLE;
+  } else if(new_state == "vanish") {
+    vanish();
+  } else {
+    std::ostringstream msg;
+    msg << "Can't set unknown willowisp state '" << new_state << "', should "
+               "be stopped, move_path, move_path_track or normal";
+    throw new std::runtime_error(msg.str());
+  }
+}
+
+void
+WillOWisp::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  std::cout << "Expose me '" << name << "'\n";
+  Scripting::WillOWisp* interface = static_cast<Scripting::WillOWisp*> (this);
+  expose_object(vm, table_idx, interface, name);
+}
+  
+void
+WillOWisp::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  std::cout << "UnExpose me '" << name << "'\n";
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+IMPLEMENT_FACTORY(WillOWisp, "willowisp")
diff --git a/src/badguy/willowisp.hpp b/src/badguy/willowisp.hpp
new file mode 100644 (file)
index 0000000..2ccbc68
--- /dev/null
@@ -0,0 +1,84 @@
+//  $Id$
+//
+//  SuperTux - "Will-O-Wisp" Badguy
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __WILLOWISP_H__
+#define __WILLOWISP_H__
+
+#include "badguy.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+#include "scripting/willowisp.hpp"
+
+class WillOWisp : public BadGuy, public Scripting::WillOWisp,
+                  public ScriptInterface
+{
+public:
+  WillOWisp(const lisp::Lisp& reader);
+
+  void activate();
+  void deactivate();
+
+  void active_update(float elapsed_time);
+  virtual bool is_flammable() const { return false; }
+  virtual bool is_freezable() const { return false; }
+  virtual void kill_fall() { vanish(); }
+
+  /**
+   * make WillOWisp vanish
+   */
+  void vanish();
+
+  virtual void draw(DrawingContext& context);
+
+  virtual void goto_node(int node_no);
+  virtual void set_state(const std::string& state);
+  virtual void start_moving();
+  virtual void stop_moving();
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+protected:
+  virtual bool collides(GameObject& other, const CollisionHit& hit);
+  HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+private:
+  enum MyState {
+    STATE_STOPPED, STATE_IDLE, STATE_TRACKING, STATE_VANISHING, STATE_WARPING,
+    STATE_PATHMOVING, STATE_PATHMOVING_TRACK
+  };
+  MyState mystate;
+
+  std::string target_sector;
+  std::string target_spawnpoint;
+  std::string hit_script;
+
+  std::auto_ptr<SoundSource> sound_source;
+
+  std::auto_ptr<Path>        path;
+  std::auto_ptr<PathWalker>  walker;
+
+  float flyspeed;
+  float track_range;
+  float vanish_range;
+};
+
+#endif
diff --git a/src/badguy/yeti.cpp b/src/badguy/yeti.cpp
new file mode 100644 (file)
index 0000000..7995925
--- /dev/null
@@ -0,0 +1,324 @@
+//  $Id$
+//
+//  SuperTux - Boss "Yeti"
+//  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <float.h>
+#include <sstream>
+#include <memory>
+#include "yeti.hpp"
+#include "object/camera.hpp"
+#include "yeti_stalactite.hpp"
+#include "bouncing_snowball.hpp"
+#include "game_session.hpp"
+#include "level.hpp"
+
+namespace {
+  const float JUMP_DOWN_VX = 250; /**< horizontal speed while jumping off the dais */
+  const float JUMP_DOWN_VY = -250; /**< vertical speed while jumping off the dais */
+
+  const float RUN_VX = 350; /**< horizontal speed while running */
+
+  const float JUMP_UP_VX = 350; /**< horizontal speed while jumping on the dais */
+  const float JUMP_UP_VY = -800; /**< vertical speed while jumping on the dais */
+
+  const float STOMP_VY = -250; /** vertical speed while stomping on the dais */
+
+  const float LEFT_STAND_X = 16; /**< x-coordinate of left dais' end position */
+  const float RIGHT_STAND_X = 800-60-16; /**< x-coordinate of right dais' end position */
+  const float LEFT_JUMP_X = LEFT_STAND_X+224; /**< x-coordinate of from where to jump on the left dais */
+  const float RIGHT_JUMP_X = RIGHT_STAND_X-224; /**< x-coordinate of from where to jump on the right dais */
+  const float STOMP_WAIT = .5; /**< time we stay on the dais before jumping again */
+  const float SAFE_TIME = .5; /**< the time we are safe when tux just hit us */
+  const int INITIAL_HITPOINTS = 3; /**< number of hits we can take */
+
+  const float SQUISH_TIME = 5;
+}
+
+Yeti::Yeti(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/yeti/yeti.sprite")
+{
+  hit_points = INITIAL_HITPOINTS;
+  countMe = false;
+  sound_manager->preload("sounds/yeti_gna.wav");
+  sound_manager->preload("sounds/yeti_roar.wav");
+  hud_head.reset(new Surface("images/creatures/yeti/hudlife.png"));
+}
+
+Yeti::~Yeti()
+{
+}
+
+void
+Yeti::activate()
+{
+  dir = RIGHT;
+  jump_down();
+}
+
+void
+Yeti::draw(DrawingContext& context)
+{
+  // we blink when we are safe
+  if(safe_timer.started() && size_t(game_time*40)%2)
+    return;
+
+  draw_hit_points(context);
+
+  BadGuy::draw(context);
+}
+
+void
+Yeti::draw_hit_points(DrawingContext& context)
+{
+  int i;
+
+  Surface *hh = hud_head.get();
+  if (!hh)
+    return;
+
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  for (i = 0; i < hit_points; ++i)
+  {
+    context.draw_surface(hh, Vector(BORDER_X + (i * hh->get_width()), BORDER_Y + 1), LAYER_FOREGROUND1);
+  }
+
+  context.pop_transform();
+}
+
+void
+Yeti::active_update(float elapsed_time)
+{
+  switch(state) {
+    case JUMP_DOWN:
+      physic.set_velocity_x((dir==RIGHT)?+JUMP_DOWN_VX:-JUMP_DOWN_VX);
+      break;
+    case RUN:
+      physic.set_velocity_x((dir==RIGHT)?+RUN_VX:-RUN_VX);
+      if (((dir == RIGHT) && (get_pos().x >= RIGHT_JUMP_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_JUMP_X))) jump_up();
+      break;
+    case JUMP_UP:
+      physic.set_velocity_x((dir==RIGHT)?+JUMP_UP_VX:-JUMP_UP_VX);
+      if (((dir == RIGHT) && (get_pos().x >= RIGHT_STAND_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_STAND_X))) be_angry();
+      break;
+    case BE_ANGRY:
+      if(state_timer.check()) {
+        sound_manager->play("sounds/yeti_gna.wav");
+        physic.set_velocity_y(STOMP_VY);
+        sprite->set_action((dir==RIGHT)?"stomp-right":"stomp-left");
+      }
+      break;
+    case SQUISHED:
+      if (state_timer.check()) {
+        remove_me();
+      }
+      break;
+  }
+
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+Yeti::jump_down()
+{
+  sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
+  physic.set_velocity_x((dir==RIGHT)?(+JUMP_DOWN_VX):(-JUMP_DOWN_VX));
+  physic.set_velocity_y(JUMP_DOWN_VY);
+  state = JUMP_DOWN;
+}
+
+void
+Yeti::run()
+{
+  sprite->set_action((dir==RIGHT)?"run-right":"run-left");
+  physic.set_velocity_x((dir==RIGHT)?(+RUN_VX):(-RUN_VX));
+  physic.set_velocity_y(0);
+  state = RUN;
+}
+
+void
+Yeti::jump_up()
+{
+  sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
+  physic.set_velocity_x((dir==RIGHT)?(+JUMP_UP_VX):(-JUMP_UP_VX));
+  physic.set_velocity_y(JUMP_UP_VY);
+  state = JUMP_UP;
+}
+
+void
+Yeti::be_angry()
+{
+  //turn around
+  dir = (dir==RIGHT)?LEFT:RIGHT;
+
+  sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+  if (hit_points < INITIAL_HITPOINTS) summon_snowball();
+  stomp_count = 0;
+  state = BE_ANGRY;
+  state_timer.start(STOMP_WAIT);
+}
+
+void
+Yeti::summon_snowball()
+{
+  Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x+(dir == RIGHT ? 64 : -64), get_pos().y), dir));
+}
+
+bool
+Yeti::collision_squished(GameObject& object)
+{
+  kill_squished(object);
+
+  return true;
+}
+
+void
+Yeti::kill_squished(GameObject& object)
+{
+  Player* player = dynamic_cast<Player*>(&object);
+  if (player) {
+    player->bounce(*this);
+    take_hit(*player);
+  }
+}
+
+void Yeti::take_hit(Player& )
+{
+  if(safe_timer.started())
+    return;
+
+  sound_manager->play("sounds/yeti_roar.wav");
+  hit_points--;
+
+  if(hit_points <= 0) {
+    // We're dead
+    physic.enable_gravity(true);
+    physic.set_velocity_x(0);
+    physic.set_velocity_y(0);
+
+    state = SQUISHED;
+    state_timer.start(SQUISH_TIME);
+    set_group(COLGROUP_MOVING_ONLY_STATIC);
+    sprite->set_action("dead");
+
+    if (countMe) Sector::current()->get_level()->stats.badguys++;
+
+    if(dead_script != "") {
+      std::istringstream stream(dead_script);
+      Sector::current()->run_script(stream, "Yeti - dead-script");
+    }
+  }
+  else {
+    safe_timer.start(SAFE_TIME);
+  }
+}
+
+void
+Yeti::kill_fall()
+{
+  // shooting bullets or being invincible won't work :)
+  take_hit(*get_nearest_player()); // FIXME: debug only(?)
+}
+
+void
+Yeti::write(lisp::Writer& writer)
+{
+  writer.start_list("yeti");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("yeti");
+}
+
+void
+Yeti::drop_stalactite()
+{
+  // make a stalactite falling down and shake camera a bit
+  Sector::current()->camera->shake(.1f, 0, 10);
+
+  YetiStalactite* nearest = 0;
+  float dist = FLT_MAX;
+
+  Player* player = this->get_nearest_player();
+  if (!player) return;
+
+  Sector* sector = Sector::current();
+  for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
+      i != sector->gameobjects.end(); ++i) {
+    YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
+    if(stalactite && stalactite->is_hanging()) {
+      float sdist
+        = fabsf(stalactite->get_pos().x - player->get_pos().x);
+      if(sdist < dist) {
+        nearest = stalactite;
+        dist = sdist;
+      }
+    }
+  }
+
+  if(nearest)
+    nearest->start_shaking();
+}
+
+void
+Yeti::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    // hit floor or roof
+    physic.set_velocity_y(0);
+    switch (state) {
+      case JUMP_DOWN:
+       run();
+       break;
+      case RUN:
+       break;
+      case JUMP_UP:
+       break;
+      case BE_ANGRY:
+       // we just landed
+       if(!state_timer.started()) {
+         sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
+         stomp_count++;
+         drop_stalactite();
+
+         // go to other side after 3 jumps
+         if(stomp_count == 3) {
+           jump_down();
+         } else {
+           // jump again
+           state_timer.start(STOMP_WAIT);
+         }
+       }
+       break;
+      case SQUISHED:
+        break;
+    }
+  } else if(hit.left || hit.right) {
+    // hit wall
+    jump_up();
+  }
+}
+
+IMPLEMENT_FACTORY(Yeti, "yeti")
diff --git a/src/badguy/yeti.hpp b/src/badguy/yeti.hpp
new file mode 100644 (file)
index 0000000..cfc6d05
--- /dev/null
@@ -0,0 +1,72 @@
+//  $Id$
+//
+//  SuperTux - Boss "Yeti"
+//  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __YETI_H__
+#define __YETI_H__
+
+#include <memory>
+
+#include "badguy.hpp"
+
+class Yeti : public BadGuy
+{
+public:
+  Yeti(const lisp::Lisp& lisp);
+  ~Yeti();
+
+  void draw(DrawingContext& context);
+  void write(lisp::Writer& writer);
+  void activate();
+  void active_update(float elapsed_time);
+  void collision_solid(const CollisionHit& hit);
+  bool collision_squished(GameObject& object);
+  void kill_squished(GameObject& object);
+  void kill_fall();
+
+  virtual Yeti* clone() const { return new Yeti((Yeti&)*this); }
+
+private:
+  void run();
+  void jump_up();
+  void be_angry();
+  void drop_stalactite();
+  void summon_snowball();
+  void jump_down();
+
+  void draw_hit_points(DrawingContext& context);
+
+  void take_hit(Player& player);
+
+  enum YetiState {
+    JUMP_DOWN,
+    RUN,
+    JUMP_UP,
+    BE_ANGRY,
+    SQUISHED
+  };
+  YetiState state;
+  Timer state_timer;
+  Timer safe_timer;
+  int stomp_count;
+  int hit_points;
+  std::auto_ptr<Surface> hud_head;
+};
+
+#endif
diff --git a/src/badguy/yeti_stalactite.cpp b/src/badguy/yeti_stalactite.cpp
new file mode 100644 (file)
index 0000000..de27da3
--- /dev/null
@@ -0,0 +1,64 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "yeti_stalactite.hpp"
+
+static const float SHAKE_TIME = .8f;
+
+YetiStalactite::YetiStalactite(const lisp::Lisp& lisp)
+  : Stalactite(lisp)
+{
+}
+
+YetiStalactite::~YetiStalactite()
+{
+}
+
+void
+YetiStalactite::write(lisp::Writer& writer)
+{
+  writer.start_list("yeti_stalactite");
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+  writer.end_list("yeti_stalactite");
+}
+
+void
+YetiStalactite::start_shaking()
+{
+  timer.start(SHAKE_TIME);
+  state = STALACTITE_SHAKING;
+}
+
+bool
+YetiStalactite::is_hanging()
+{
+  return state == STALACTITE_HANGING;
+}
+
+void
+YetiStalactite::active_update(float elapsed_time)
+{
+  if(state == STALACTITE_HANGING)
+    return;
+
+  Stalactite::active_update(elapsed_time);
+}
+
+IMPLEMENT_FACTORY(YetiStalactite, "yeti_stalactite")
diff --git a/src/badguy/yeti_stalactite.hpp b/src/badguy/yeti_stalactite.hpp
new file mode 100644 (file)
index 0000000..04897c0
--- /dev/null
@@ -0,0 +1,40 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+#ifndef __YETI_STALACTITE_H__
+#define __YETI_STALACTITE_H__
+
+#include "stalactite.hpp"
+
+class YetiStalactite : public Stalactite
+{
+public:
+  YetiStalactite(const lisp::Lisp& lisp);
+  virtual ~YetiStalactite();
+
+  void write(lisp::Writer& );
+  void active_update(float elapsed_time);
+  void start_shaking();
+  bool is_hanging();
+
+  virtual YetiStalactite* clone() const { return new YetiStalactite(*this); }
+};
+
+#endif
diff --git a/src/badguy/zeekling.cpp b/src/badguy/zeekling.cpp
new file mode 100644 (file)
index 0000000..64a4566
--- /dev/null
@@ -0,0 +1,196 @@
+//  $Id$
+//
+//  Zeekling - flyer that swoops down when she spots the player
+//  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+#include <math.h>
+
+#include "zeekling.hpp"
+#include "random_generator.hpp"
+
+Zeekling::Zeekling(const lisp::Lisp& reader)
+       : BadGuy(reader, "images/creatures/zeekling/zeekling.sprite"), last_player(0)
+{
+  state = FLYING;
+}
+
+Zeekling::Zeekling(const Vector& pos, Direction d)
+       : BadGuy(pos, d, "images/creatures/zeekling/zeekling.sprite"), last_player(0)
+{
+  state = FLYING;
+}
+
+void
+Zeekling::write(lisp::Writer& writer)
+{
+  writer.start_list("zeekling");
+
+  writer.write_float("x", start_position.x);
+  writer.write_float("y", start_position.y);
+
+  writer.end_list("zeekling");
+}
+
+void
+Zeekling::activate()
+{
+  speed = systemRandom.rand(130, 171);
+  physic.set_velocity_x(dir == LEFT ? -speed : speed);
+  physic.enable_gravity(false);
+  sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+bool
+Zeekling::collision_squished(GameObject& object)
+{
+  sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+  kill_squished(object);
+  kill_fall();
+  return true;
+}
+
+void
+Zeekling::onBumpHorizontal() {
+  if (state == FLYING) {
+    dir = (dir == LEFT ? RIGHT : LEFT);
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(dir == LEFT ? -speed : speed);
+  } else
+  if (state == DIVING) {
+    dir = (dir == LEFT ? RIGHT : LEFT);
+    state = FLYING;
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(dir == LEFT ? -speed : speed);
+    physic.set_velocity_y(0);
+  } else
+  if (state == CLIMBING) {
+    dir = (dir == LEFT ? RIGHT : LEFT);
+    sprite->set_action(dir == LEFT ? "left" : "right");
+    physic.set_velocity_x(dir == LEFT ? -speed : speed);
+  } else {
+    assert(false);
+  }
+}
+
+void
+Zeekling::onBumpVertical() {
+  if (state == FLYING) {
+    physic.set_velocity_y(0);
+  } else
+  if (state == DIVING) {
+    state = CLIMBING;
+    physic.set_velocity_y(-speed);
+    sprite->set_action(dir == LEFT ? "left" : "right");
+  } else
+  if (state == CLIMBING) {
+    state = FLYING;
+    physic.set_velocity_y(0);
+  }
+}
+
+void
+Zeekling::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    onBumpVertical();
+  } else if(hit.left || hit.right) {
+    onBumpHorizontal();
+  }
+}
+
+/**
+ * linear prediction of player and badguy positions to decide if we should enter the DIVING state
+ */
+bool
+Zeekling::should_we_dive() {
+
+  const MovingObject* player = this->get_nearest_player();
+  if (player && last_player && (player == last_player)) {
+
+    // get positions, calculate movement
+    const Vector player_pos = player->get_pos();
+    const Vector player_mov = (player_pos - last_player_pos);
+    const Vector self_pos = this->get_pos();
+    const Vector self_mov = (self_pos - last_self_pos);
+
+    // new vertical speed to test with
+    float vy = 2*fabsf(self_mov.x);
+
+    // do not dive if we are not above the player
+    float height = player_pos.y - self_pos.y;
+    if (height <= 0) return false;
+
+    // do not dive if we are too far above the player
+    if (height > 512) return false;
+
+    // do not dive if we would not descend faster than the player
+    float relSpeed = vy - player_mov.y;
+    if (relSpeed <= 0) return false;
+
+    // guess number of frames to descend to same height as player
+    float estFrames = height / relSpeed;
+
+    // guess where the player would be at this time
+    float estPx = (player_pos.x + (estFrames * player_mov.x));
+
+    // guess where we would be at this time
+    float estBx = (self_pos.x + (estFrames * self_mov.x));
+
+    // near misses are OK, too
+    if (fabsf(estPx - estBx) < 8) return true;
+  }
+
+  // update last player tracked, as well as our positions
+  last_player = player;
+  if (player) {
+    last_player_pos = player->get_pos();
+    last_self_pos = this->get_pos();
+  }
+
+  return false;
+}
+
+void
+Zeekling::active_update(float elapsed_time) {
+  if (state == FLYING) {
+    if (should_we_dive()) {
+      state = DIVING;
+      physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
+      sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
+    }
+    BadGuy::active_update(elapsed_time);
+    return;
+  } else if (state == DIVING) {
+    BadGuy::active_update(elapsed_time);
+    return;
+  } else if (state == CLIMBING) {
+    // stop climbing when we're back at initial height
+    if (get_pos().y <= start_position.y) {
+      state = FLYING;
+      physic.set_velocity_y(0);
+    }
+    BadGuy::active_update(elapsed_time);
+    return;
+  } else {
+    assert(false);
+  }
+}
+
+IMPLEMENT_FACTORY(Zeekling, "zeekling")
diff --git a/src/badguy/zeekling.hpp b/src/badguy/zeekling.hpp
new file mode 100644 (file)
index 0000000..a130179
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  Zeekling - flyer that swoops down when she spots the player
+//  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __ZEEKLING_H__
+#define __ZEEKLING_H__
+
+#include "badguy.hpp"
+
+class Zeekling : public BadGuy
+{
+public:
+  Zeekling(const lisp::Lisp& reader);
+  Zeekling(const Vector& pos, Direction d);
+
+  void activate();
+  void write(lisp::Writer& writer);
+  void collision_solid(const CollisionHit& hit);
+  void active_update(float elapsed_time);
+
+  virtual Zeekling* clone() const { return new Zeekling(*this); }
+
+protected:
+  bool collision_squished(GameObject& object);
+  float speed;
+
+  Timer diveRecoverTimer;
+
+  enum ZeeklingState {
+    FLYING,
+    DIVING,
+    CLIMBING
+  };
+  ZeeklingState state;
+
+private:
+  const MovingObject* last_player; /**< last player we tracked */
+  Vector last_player_pos; /**< position we last spotted the player at */
+  Vector last_self_pos; /**< position we last were at */
+
+  bool should_we_dive();
+  void onBumpHorizontal();
+  void onBumpVertical();
+};
+
+#endif
diff --git a/src/binreloc/binreloc.c b/src/binreloc/binreloc.c
new file mode 100644 (file)
index 0000000..17df59e
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_C__
+#define __BINRELOC_C__
+
+// [Christoph] use config.h, which defines ENABLE_BINRELOC
+#include <config.h>
+
+#ifdef ENABLE_BINRELOC
+       #include <sys/types.h>
+       #include <sys/stat.h>
+       #include <unistd.h>
+#endif /* ENABLE_BINRELOC */
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include "binreloc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *
+_br_find_exe (BrInitError *error)
+{
+#ifndef ENABLE_BINRELOC
+       if (error)
+               *error = BR_INIT_ERROR_DISABLED;
+       return NULL;
+#else
+       char *path, *path2, *line, *result;
+       size_t buf_size;
+       ssize_t size;
+       struct stat stat_buf;
+       FILE *f;
+
+       /* Read from /proc/self/exe (symlink) */
+       if (sizeof (path) > SSIZE_MAX)
+               buf_size = SSIZE_MAX - 1;
+       else
+               buf_size = PATH_MAX - 1;
+       path = (char *) malloc (buf_size);
+       if (path == NULL) {
+               /* Cannot allocate memory. */
+               if (error)
+                       *error = BR_INIT_ERROR_NOMEM;
+               return NULL;
+       }
+       path2 = (char *) malloc (buf_size);
+       if (path2 == NULL) {
+               /* Cannot allocate memory. */
+               if (error)
+                       *error = BR_INIT_ERROR_NOMEM;
+               free (path);
+               return NULL;
+       }
+
+       strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+       while (1) {
+               int i;
+
+               size = readlink (path2, path, buf_size - 1);
+               if (size == -1) {
+                       /* Error. */
+                       free (path2);
+                       break;
+               }
+
+               /* readlink() success. */
+               path[size] = '\0';
+
+               /* Check whether the symlink's target is also a symlink.
+                * We want to get the final target. */
+               i = stat (path, &stat_buf);
+               if (i == -1) {
+                       /* Error. */
+                       free (path2);
+                       break;
+               }
+
+               /* stat() success. */
+               if (!S_ISLNK (stat_buf.st_mode)) {
+                       /* path is not a symlink. Done. */
+                       free (path2);
+                       return path;
+               }
+
+               /* path is a symlink. Continue loop and resolve this. */
+               strncpy (path, path2, buf_size - 1);
+       }
+
+
+       /* readlink() or stat() failed; this can happen when the program is
+        * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+       buf_size = PATH_MAX + 128;
+       line = (char *) realloc (path, buf_size);
+       if (line == NULL) {
+               /* Cannot allocate memory. */
+               free (path);
+               if (error)
+                       *error = BR_INIT_ERROR_NOMEM;
+               return NULL;
+       }
+
+       f = fopen ("/proc/self/maps", "r");
+       if (f == NULL) {
+               free (line);
+               if (error)
+                       *error = BR_INIT_ERROR_OPEN_MAPS;
+               return NULL;
+       }
+
+       /* The first entry should be the executable name. */
+       result = fgets (line, (int) buf_size, f);
+       if (result == NULL) {
+               fclose (f);
+               free (line);
+               if (error)
+                       *error = BR_INIT_ERROR_READ_MAPS;
+               return NULL;
+       }
+
+       /* Get rid of newline character. */
+       buf_size = strlen (line);
+       if (buf_size <= 0) {
+               /* Huh? An empty string? */
+               fclose (f);
+               free (line);
+               if (error)
+                       *error = BR_INIT_ERROR_INVALID_MAPS;
+               return NULL;
+       }
+       if (line[buf_size - 1] == 10)
+               line[buf_size - 1] = 0;
+
+       /* Extract the filename; it is always an absolute path. */
+       path = strchr (line, '/');
+
+       /* Sanity check. */
+       if (strstr (line, " r-xp ") == NULL || path == NULL) {
+               fclose (f);
+               free (line);
+               if (error)
+                       *error = BR_INIT_ERROR_INVALID_MAPS;
+               return NULL;
+       }
+
+       path = strdup (path);
+       free (line);
+       fclose (f);
+       return path;
+#endif /* ENABLE_BINRELOC */
+}
+
+
+/** @internal
+ * Find the canonical filename of the executable which owns symbol.
+ * Returns a filename which must be freed, or NULL on error.
+ */
+static char *
+_br_find_exe_for_symbol (const void *symbol, BrInitError *error)
+{
+       symbol = symbol; // [Christoph] mark it as used
+#ifndef ENABLE_BINRELOC
+       if (error)
+               *error = BR_INIT_ERROR_DISABLED;
+       return (char *) NULL;
+#else
+       #define SIZE PATH_MAX + 100
+       FILE *f;
+       size_t address_string_len;
+       char *address_string, line[SIZE], *found;
+
+       if (symbol == NULL)
+               return (char *) NULL;
+
+       f = fopen ("/proc/self/maps", "r");
+       if (f == NULL)
+               return (char *) NULL;
+
+       address_string_len = 4;
+       address_string = (char *) malloc (address_string_len);
+       found = (char *) NULL;
+
+       while (!feof (f)) {
+               char *start_addr, *end_addr, *end_addr_end, *file;
+               void *start_addr_p, *end_addr_p;
+               size_t len;
+
+               if (fgets (line, SIZE, f) == NULL)
+                       break;
+
+               /* Sanity check. */
+               if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
+                       continue;
+
+               /* Parse line. */
+               start_addr = line;
+               end_addr = strchr (line, '-');
+               file = strchr (line, '/');
+
+               /* More sanity check. */
+               if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
+                       continue;
+
+               end_addr[0] = '\0';
+               end_addr++;
+               end_addr_end = strchr (end_addr, ' ');
+               if (end_addr_end == NULL)
+                       continue;
+
+               end_addr_end[0] = '\0';
+               len = strlen (file);
+               if (len == 0)
+                       continue;
+               if (file[len - 1] == '\n')
+                       file[len - 1] = '\0';
+
+               /* Get rid of "(deleted)" from the filename. */
+               len = strlen (file);
+               if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
+                       file[len - 10] = '\0';
+
+               /* I don't know whether this can happen but better safe than sorry. */
+               len = strlen (start_addr);
+               if (len != strlen (end_addr))
+                       continue;
+
+
+               /* Transform the addresses into a string in the form of 0xdeadbeef,
+                * then transform that into a pointer. */
+               if (address_string_len < len + 3) {
+                       address_string_len = len + 3;
+                       address_string = (char *) realloc (address_string, address_string_len);
+               }
+
+               memcpy (address_string, "0x", 2);
+               memcpy (address_string + 2, start_addr, len);
+               address_string[2 + len] = '\0';
+               sscanf (address_string, "%p", &start_addr_p);
+
+               memcpy (address_string, "0x", 2);
+               memcpy (address_string + 2, end_addr, len);
+               address_string[2 + len] = '\0';
+               sscanf (address_string, "%p", &end_addr_p);
+
+
+               if (symbol >= start_addr_p && symbol < end_addr_p) {
+                       found = file;
+                       break;
+               }
+       }
+
+       free (address_string);
+       fclose (f);
+
+       if (found == NULL)
+               return (char *) NULL;
+       else
+               return strdup (found);
+#endif /* ENABLE_BINRELOC */
+}
+
+
+#ifndef BINRELOC_RUNNING_DOXYGEN
+       #undef NULL
+       #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
+#endif
+
+static char *exe = (char *) NULL;
+
+
+/** Initialize the BinReloc library (for applications).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the application's canonical filename.
+ *
+ * @note If you want to use BinReloc for a library, then you should call
+ *       br_init_lib() instead.
+ *
+ * @param error  If BinReloc failed to initialize, then the error code will
+ *               be stored in this variable. Set to NULL if you want to
+ *               ignore this. See #BrInitError for a list of error codes.
+ *
+ * @returns 1 on success, 0 if BinReloc failed to initialize.
+ */
+int
+br_init (BrInitError *error)
+{
+       exe = _br_find_exe (error);
+       return exe != NULL;
+}
+
+
+/** Initialize the BinReloc library (for libraries).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the calling library's canonical filename.
+ *
+ * @note The BinReloc source code MUST be included in your library, or this
+ *       function won't work correctly.
+ *
+ * @param error  If BinReloc failed to initialize, then the error code will
+ *               be stored in this variable. Set to NULL if you want to
+ *               ignore this. See #BrInitError for a list of error codes.
+ *
+ * @returns 1 on success, 0 if a filename cannot be found.
+ */
+int
+br_init_lib (BrInitError *error)
+{
+       exe = _br_find_exe_for_symbol ((const void *) "", error);
+       return exe != NULL;
+}
+
+
+/** Find the canonical filename of the current application.
+ *
+ * @param default_exe  A default filename which will be used as fallback.
+ * @returns A string containing the application's canonical filename,
+ *          which must be freed when no longer necessary. If BinReloc is
+ *          not initialized, or if br_init() failed, then a copy of
+ *          default_exe will be returned. If default_exe is NULL, then
+ *          NULL will be returned.
+ */
+char *
+br_find_exe (const char *default_exe)
+{
+       if (exe == (char *) NULL) {
+               /* BinReloc is not initialized. */
+               if (default_exe != (const char *) NULL)
+                       return strdup (default_exe);
+               else
+                       return (char *) NULL;
+       }
+       return strdup (exe);
+}
+
+
+/** Locate the directory in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(exename)
+ * \endcode
+ *
+ * @param default_dir  A default directory which will used as fallback.
+ * @return A string containing the directory, which must be freed when no
+ *         longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_dir
+ *         will be returned. If default_dir is NULL, then NULL will be
+ *         returned.
+ */
+char *
+br_find_exe_dir (const char *default_dir)
+{
+       if (exe == NULL) {
+               /* BinReloc not initialized. */
+               if (default_dir != NULL)
+                       return strdup (default_dir);
+               else
+                       return NULL;
+       }
+
+       return br_dirname (exe);
+}
+
+
+/** Locate the prefix in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(dirname(exename))
+ * \endcode
+ *
+ * @param default_prefix  A default prefix which will used as fallback.
+ * @return A string containing the prefix, which must be freed when no
+ *         longer necessary. If BinReloc is not initialized, or if
+ *         the initialization function failed, then a copy of default_prefix
+ *         will be returned. If default_prefix is NULL, then NULL will be returned.
+ */
+char *
+br_find_prefix (const char *default_prefix)
+{
+       char *dir1, *dir2;
+
+       if (exe == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_prefix != (const char *) NULL)
+                       return strdup (default_prefix);
+               else
+                       return (char *) NULL;
+       }
+
+       dir1 = br_dirname (exe);
+       dir2 = br_dirname (dir1);
+       free (dir1);
+       return dir2;
+}
+
+
+/** Locate the application's binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/bin"
+ * \endcode
+ *
+ * @param default_bin_dir  A default path which will used as fallback.
+ * @return A string containing the bin folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if
+ *         the initialization function failed, then a copy of default_bin_dir will
+ *         be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_bin_dir (const char *default_bin_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_bin_dir != (const char *) NULL)
+                       return strdup (default_bin_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "bin");
+       free (prefix);
+       return dir;
+}
+
+
+/** Locate the application's superuser binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/sbin"
+ * \endcode
+ *
+ * @param default_sbin_dir  A default path which will used as fallback.
+ * @return A string containing the sbin folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_sbin_dir will
+ *         be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_sbin_dir (const char *default_sbin_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_sbin_dir != (const char *) NULL)
+                       return strdup (default_sbin_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "sbin");
+       free (prefix);
+       return dir;
+}
+
+
+/** Locate the application's data folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share"
+ * \endcode
+ *
+ * @param default_data_dir  A default path which will used as fallback.
+ * @return A string containing the data folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_data_dir
+ *         will be returned. If default_data_dir is NULL, then NULL will be
+ *         returned.
+ */
+char *
+br_find_data_dir (const char *default_data_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_data_dir != (const char *) NULL)
+                       return strdup (default_data_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "share");
+       free (prefix);
+       return dir;
+}
+
+
+/** Locate the application's localization folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share/locale"
+ * \endcode
+ *
+ * @param default_locale_dir  A default path which will used as fallback.
+ * @return A string containing the localization folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_locale_dir will be returned.
+ *         If default_locale_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_locale_dir (const char *default_locale_dir)
+{
+       char *data_dir, *dir;
+
+       data_dir = br_find_data_dir ((const char *) NULL);
+       if (data_dir == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_locale_dir != (const char *) NULL)
+                       return strdup (default_locale_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (data_dir, "locale");
+       free (data_dir);
+       return dir;
+}
+
+
+/** Locate the application's library folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/lib"
+ * \endcode
+ *
+ * @param default_lib_dir  A default path which will used as fallback.
+ * @return A string containing the library folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the initialization
+ *         function failed, then a copy of default_lib_dir will be returned.
+ *         If default_lib_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_lib_dir (const char *default_lib_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_lib_dir != (const char *) NULL)
+                       return strdup (default_lib_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "lib");
+       free (prefix);
+       return dir;
+}
+
+
+/** Locate the application's libexec folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/libexec"
+ * \endcode
+ *
+ * @param default_libexec_dir  A default path which will used as fallback.
+ * @return A string containing the libexec folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the initialization
+ *         function failed, then a copy of default_libexec_dir will be returned.
+ *         If default_libexec_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_libexec_dir (const char *default_libexec_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_libexec_dir != (const char *) NULL)
+                       return strdup (default_libexec_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "libexec");
+       free (prefix);
+       return dir;
+}
+
+
+/** Locate the application's configuration files folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/etc"
+ * \endcode
+ *
+ * @param default_etc_dir  A default path which will used as fallback.
+ * @return A string containing the etc folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the initialization
+ *         function failed, then a copy of default_etc_dir will be returned.
+ *         If default_etc_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_etc_dir (const char *default_etc_dir)
+{
+       char *prefix, *dir;
+
+       prefix = br_find_prefix ((const char *) NULL);
+       if (prefix == (char *) NULL) {
+               /* BinReloc not initialized. */
+               if (default_etc_dir != (const char *) NULL)
+                       return strdup (default_etc_dir);
+               else
+                       return (char *) NULL;
+       }
+
+       dir = br_build_path (prefix, "etc");
+       free (prefix);
+       return dir;
+}
+
+
+/***********************
+ * Utility functions
+ ***********************/
+
+/** Concatenate str1 and str2 to a newly allocated string.
+ *
+ * @param str1 A string.
+ * @param str2 Another string.
+ * @returns A newly-allocated string. This string should be freed when no longer needed.
+ */
+char *
+br_strcat (const char *str1, const char *str2)
+{
+       char *result;
+       size_t len1, len2;
+
+       if (str1 == NULL)
+               str1 = "";
+       if (str2 == NULL)
+               str2 = "";
+
+       len1 = strlen (str1);
+       len2 = strlen (str2);
+
+       result = (char *) malloc (len1 + len2 + 1);
+       memcpy (result, str1, len1);
+       memcpy (result + len1, str2, len2);
+       result[len1 + len2] = '\0';
+
+       return result;
+}
+
+
+char *
+br_build_path (const char *dir, const char *file)
+{
+       char *dir2, *result;
+       size_t len;
+       int must_free = 0;
+
+       len = strlen (dir);
+       if (len > 0 && dir[len - 1] != '/') {
+               dir2 = br_strcat (dir, "/");
+               must_free = 1;
+       } else
+               dir2 = (char *) dir;
+
+       result = br_strcat (dir2, file);
+       if (must_free)
+               free (dir2);
+       return result;
+}
+
+
+/* Emulates glibc's strndup() */
+static char *
+br_strndup (const char *str, size_t size)
+{
+       char *result = (char *) NULL;
+       size_t len;
+
+       if (str == (const char *) NULL)
+               return (char *) NULL;
+
+       len = strlen (str);
+       if (len == 0)
+               return strdup ("");
+       if (size > len)
+               size = len;
+
+       result = (char *) malloc (len + 1);
+       memcpy (result, str, size);
+       result[size] = '\0';
+       return result;
+}
+
+
+/** Extracts the directory component of a path.
+ *
+ * Similar to g_dirname() or the dirname commandline application.
+ *
+ * Example:
+ * \code
+ * br_dirname ("/usr/local/foobar");  --> Returns: "/usr/local"
+ * \endcode
+ *
+ * @param path  A path.
+ * @returns     A directory name. This string should be freed when no longer needed.
+ */
+char *
+br_dirname (const char *path)
+{
+       char *end, *result;
+
+       if (path == (const char *) NULL)
+               return (char *) NULL;
+
+       end = strrchr (path, '/');
+       if (end == (const char *) NULL)
+               return strdup (".");
+
+       while (end > path && *end == '/')
+               end--;
+       result = br_strndup (path, end - path + 1);
+       if (result[0] == 0) {
+               free (result);
+               return strdup ("/");
+       } else
+               return result;
+}
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BINRELOC_C__ */
diff --git a/src/binreloc/binreloc.h b/src/binreloc/binreloc.h
new file mode 100644 (file)
index 0000000..592d998
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_H__
+#define __BINRELOC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+       /** Cannot allocate memory. */
+       BR_INIT_ERROR_NOMEM,
+       /** Unable to open /proc/self/maps; see errno for details. */
+       BR_INIT_ERROR_OPEN_MAPS,
+       /** Unable to read from /proc/self/maps; see errno for details. */
+       BR_INIT_ERROR_READ_MAPS,
+       /** The file format of /proc/self/maps is invalid; kernel bug? */
+       BR_INIT_ERROR_INVALID_MAPS,
+       /** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+       BR_INIT_ERROR_DISABLED
+} BrInitError;
+
+
+#ifndef BINRELOC_RUNNING_DOXYGEN
+/* Mangle symbol names to avoid symbol collisions with other ELF objects. */
+       #define br_init             PTeH3518859728963_br_init
+       #define br_init_lib         PTeH3518859728963_br_init_lib
+       #define br_find_exe         PTeH3518859728963_br_find_exe
+       #define br_find_exe_dir     PTeH3518859728963_br_find_exe_dir
+       #define br_find_prefix      PTeH3518859728963_br_find_prefix
+       #define br_find_bin_dir     PTeH3518859728963_br_find_bin_dir
+       #define br_find_sbin_dir    PTeH3518859728963_br_find_sbin_dir
+       #define br_find_data_dir    PTeH3518859728963_br_find_data_dir
+       #define br_find_locale_dir  PTeH3518859728963_br_find_locale_dir
+       #define br_find_lib_dir     PTeH3518859728963_br_find_lib_dir
+       #define br_find_libexec_dir PTeH3518859728963_br_find_libexec_dir
+       #define br_find_etc_dir     PTeH3518859728963_br_find_etc_dir
+       #define br_strcat           PTeH3518859728963_br_strcat
+       #define br_build_path       PTeH3518859728963_br_build_path
+       #define br_dirname          PTeH3518859728963_br_dirname
+
+
+#endif
+int   br_init             (BrInitError *error);
+int   br_init_lib         (BrInitError *error);
+
+char *br_find_exe         (const char *default_exe);
+char *br_find_exe_dir     (const char *default_dir);
+char *br_find_prefix      (const char *default_prefix);
+char *br_find_bin_dir     (const char *default_bin_dir);
+char *br_find_sbin_dir    (const char *default_sbin_dir);
+char *br_find_data_dir    (const char *default_data_dir);
+char *br_find_locale_dir  (const char *default_locale_dir);
+char *br_find_lib_dir     (const char *default_lib_dir);
+char *br_find_libexec_dir (const char *default_libexec_dir);
+char *br_find_etc_dir     (const char *default_etc_dir);
+
+/* Utility functions */
+char *br_strcat  (const char *str1, const char *str2);
+char *br_build_path (const char *dir, const char *file);
+char *br_dirname (const char *path);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BINRELOC_H__ */
diff --git a/src/collision.cpp b/src/collision.cpp
new file mode 100644 (file)
index 0000000..f96fdea
--- /dev/null
@@ -0,0 +1,188 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "collision.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "math/vector.hpp"
+#include "math/aatriangle.hpp"
+#include "math/rect.hpp"
+#include "collision_hit.hpp"
+#include "log.hpp"
+
+namespace collision
+{
+
+bool intersects(const Rect& r1, const Rect& r2)
+{
+  if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
+    return false;
+  if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
+    return false;
+
+  return true;
+}
+
+//---------------------------------------------------------------------------
+
+namespace {
+  inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
+  {
+    n = Vector(p2.y-p1.y, p1.x-p2.x);
+    c = -(p2 * n);
+    float nval = n.norm();
+    n /= nval;
+    c /= nval;
+  }
+
+  static const float DELTA = .0001f;
+}
+
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+    const AATriangle& triangle)
+{
+  if(!intersects(rect, (const Rect&) triangle))
+    return false;
+
+  Vector normal;
+  float c;
+  Vector p1;
+  Rect area;
+  switch(triangle.dir & AATriangle::DEFORM_MASK) {
+    case 0:
+      area.p1 = triangle.p1;
+      area.p2 = triangle.p2;
+      break;
+    case AATriangle::DEFORM1:
+      area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
+      area.p2 = triangle.p2;
+      break;
+    case AATriangle::DEFORM2:
+      area.p1 = triangle.p1;
+      area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+      break;
+    case AATriangle::DEFORM3:
+      area.p1 = triangle.p1;
+      area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+      break;
+    case AATriangle::DEFORM4:
+      area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
+      area.p2 = triangle.p2;
+      break;
+    default:
+      assert(false);
+  }
+
+  switch(triangle.dir & AATriangle::DIRECTION_MASK) {
+    case AATriangle::SOUTHWEST:
+      p1 = Vector(rect.p1.x, rect.p2.y);
+      makePlane(area.p1, area.p2, normal, c);
+      break;
+    case AATriangle::NORTHEAST:
+      p1 = Vector(rect.p2.x, rect.p1.y);
+      makePlane(area.p2, area.p1, normal, c);
+      break;
+    case AATriangle::SOUTHEAST:
+      p1 = rect.p2;
+      makePlane(Vector(area.p1.x, area.p2.y),
+          Vector(area.p2.x, area.p1.y), normal, c);
+      break;
+    case AATriangle::NORTHWEST:
+      p1 = rect.p1;
+      makePlane(Vector(area.p2.x, area.p1.y),
+          Vector(area.p1.x, area.p2.y), normal, c);
+      break;
+    default:
+      assert(false);
+  }
+
+  float n_p1 = -(normal * p1);
+  float depth = n_p1 - c;
+  if(depth < 0)
+    return false;
+
+#if 0
+  std::cout << "R: " << rect << " Tri: " << triangle << "\n";
+  std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
+#endif
+
+  Vector outvec = normal * (depth + 0.2f);
+
+  const float RDELTA = 3;
+  if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
+        || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
+    set_rectangle_rectangle_constraints(constraints, rect, area);
+    constraints->hit.left = false;
+    constraints->hit.right = false;
+  } else {
+    if(outvec.x < 0) {
+      constraints->right = rect.get_right() + outvec.x;
+    } else {
+      constraints->left = rect.get_left() + outvec.x;
+    }
+
+    if(outvec.y < 0) {
+      constraints->bottom = rect.get_bottom() + outvec.y;
+      constraints->hit.bottom = true;
+    } else {
+      constraints->top = rect.get_top() + outvec.y;
+      constraints->hit.top = true;
+    }
+    constraints->hit.slope_normal = normal;
+  }
+
+  return true;
+}
+
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+        const Rect& r1, const Rect& r2)
+{
+  float itop = r1.get_bottom() - r2.get_top();
+  float ibottom = r2.get_bottom() - r1.get_top();
+  float ileft = r1.get_right() - r2.get_left();
+  float iright = r2.get_right() - r1.get_left();
+
+  float vert_penetration = std::min(itop, ibottom);
+  float horiz_penetration = std::min(ileft, iright);
+  if(vert_penetration < horiz_penetration) {
+    if(itop < ibottom) {
+      constraints->bottom = std::min(constraints->bottom, r2.get_top());
+      constraints->hit.bottom = true;
+    } else {
+      constraints->top = std::max(constraints->top, r2.get_bottom());
+      constraints->hit.top = true;
+    }
+  } else {
+    if(ileft < iright) {
+      constraints->right = std::min(constraints->right, r2.get_left());
+      constraints->hit.right = true;
+    } else {
+      constraints->left = std::max(constraints->left, r2.get_right());
+      constraints->hit.left = true;
+    }
+  }
+}
+
+}
diff --git a/src/collision.hpp b/src/collision.hpp
new file mode 100644 (file)
index 0000000..ba6b6f8
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __COLLISION_H__
+#define __COLLISION_H__
+
+#include <float.h>
+#include "collision_hit.hpp"
+#include <limits>
+
+class Vector;
+class Rect;
+class AATriangle;
+
+namespace collision
+{
+
+class Constraints
+{
+public:
+  Constraints() {
+    float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+    left = -infinity;
+    right = infinity;
+    top = -infinity;
+    bottom = infinity;
+  }
+
+  bool has_constraints() const {
+    float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+    return left > -infinity || right < infinity
+        || top > -infinity || bottom < infinity;
+  }
+
+  float left;
+  float right;
+  float top;
+  float bottom;
+  Vector ground_movement;
+  CollisionHit hit;
+};
+
+/** checks if 2 rectangle intersect each other */
+bool intersects(const Rect& r1, const Rect& r2);
+
+/** does collision detection between a rectangle and an axis aligned triangle
+ * Returns true in case of a collision and fills in the hit structure then.
+ */
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+                                   const AATriangle& triangle);
+
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+        const Rect& r1, const Rect& r2);
+
+}
+
+#endif
diff --git a/src/collision_hit.hpp b/src/collision_hit.hpp
new file mode 100644 (file)
index 0000000..0287903
--- /dev/null
@@ -0,0 +1,69 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_COLLISION_HIT_H
+#define SUPERTUX_COLLISION_HIT_H
+
+#include <float.h>
+#include <math.h>
+#include "math/vector.hpp"
+
+/**
+ * Used as return value for the collision functions, to indicate how the
+ * collision should be handled
+ */
+enum HitResponse
+{
+  /// don't move the object
+  ABORT_MOVE = 0,
+  /// move object out of collision and check for collisions again
+  /// if this happens to often then the move will just be aborted
+  CONTINUE,
+  /// do the move ignoring the collision
+  FORCE_MOVE,
+  /// passes movement to collided object
+  PASS_MOVEMENT,
+
+  /// the object should not appear solid
+  PASSTHROUGH,
+  /// the object should appear solid
+  SOLID,
+};
+
+/**
+ * This class collects data about a collision
+ */
+class CollisionHit
+{
+public:
+  CollisionHit() {
+    left = false;
+    right = false;
+    top = false;
+    bottom = false;
+    crush = false;
+  }
+
+  bool left, right;
+  bool top, bottom;
+  bool crush;
+
+  Vector slope_normal;
+};
+
+#endif
diff --git a/src/console.cpp b/src/console.cpp
new file mode 100644 (file)
index 0000000..28ea36c
--- /dev/null
@@ -0,0 +1,525 @@
+//  $Id$
+//
+//  SuperTux - Console
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <iostream>
+#include <math.h>
+#include <SDL_timer.h>
+#include <SDL_keyboard.h>
+#include "console.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "scripting/squirrel_error.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "player_status.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "resources.hpp"
+#include "gameconfig.hpp"
+
+/// speed (pixels/s) the console closes
+static const float FADE_SPEED = 1;
+
+Console::Console()
+  : history_position(history.end()), vm(NULL), backgroundOffset(0),
+    height(0), alpha(1.0), offset(0), focused(false), stayOpen(0) {
+  fontheight = 8;
+}
+
+Console::~Console()
+{
+  if(vm != NULL) {
+    sq_release(Scripting::global_vm, &vm_object);
+  }
+}
+
+void
+Console::init_graphics()
+{
+  font.reset(new Font(Font::FIXED,
+                      "images/engine/fonts/andale12.png",
+                      "images/engine/fonts/andale12-shadow.png", 7, 14, 1));
+  fontheight = font->get_height();
+  background.reset(new Surface("images/engine/console.png"));
+  background2.reset(new Surface("images/engine/console2.png"));
+}
+
+void
+Console::flush(ConsoleStreamBuffer* buffer)
+{
+  if (buffer == &outputBuffer) {
+    std::string s = outputBuffer.str();
+    if ((s.length() > 0) && ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r'))) {
+      while ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r')) s.erase(s.length()-1);
+      addLines(s);
+      outputBuffer.str(std::string());
+    }
+  }
+}
+
+void
+Console::ready_vm()
+{
+  if(vm == NULL) {
+    vm = Scripting::global_vm;
+    HSQUIRRELVM new_vm = sq_newthread(vm, 16);
+    if(new_vm == NULL)
+      throw Scripting::SquirrelError(vm, "Couldn't create new VM thread for console");
+
+    // store reference to thread
+    sq_resetobject(&vm_object);
+    if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
+      throw Scripting::SquirrelError(vm, "Couldn't get vm object for console");
+    sq_addref(vm, &vm_object);
+    sq_pop(vm, 1);
+
+    // create new roottable for thread
+    sq_newtable(new_vm);
+    sq_pushroottable(new_vm);
+    if(SQ_FAILED(sq_setdelegate(new_vm, -2)))
+      throw Scripting::SquirrelError(new_vm, "Couldn't set console_table delegate");
+
+    sq_setroottable(new_vm);
+
+    vm = new_vm;
+
+    try {
+      std::string filename = "scripts/console.nut";
+      IFileStream stream(filename);
+      Scripting::compile_and_run(vm, stream, filename);
+    } catch(std::exception& e) {
+      log_warning << "Couldn't load console.nut: " << e.what() << std::endl;
+    }
+  }
+}
+
+void
+Console::execute_script(const std::string& command)
+{
+  using namespace Scripting;
+
+  ready_vm();
+
+  SQInteger oldtop = sq_gettop(vm);
+  try {
+    if(SQ_FAILED(sq_compilebuffer(vm, command.c_str(), command.length(),
+                 "", SQTrue)))
+      throw SquirrelError(vm, "Couldn't compile command");
+
+    sq_pushroottable(vm);
+    if(SQ_FAILED(sq_call(vm, 1, SQTrue, SQTrue)))
+      throw SquirrelError(vm, "Problem while executing command");
+
+    if(sq_gettype(vm, -1) != OT_NULL)
+      addLines(squirrel2string(vm, -1));
+  } catch(std::exception& e) {
+    addLines(e.what());
+  }
+  SQInteger newtop = sq_gettop(vm);
+  if(newtop < oldtop) {
+    log_fatal << "Script destroyed squirrel stack..." << std::endl;
+  } else {
+    sq_settop(vm, oldtop);
+  }
+}
+
+void 
+Console::input(char c)
+{
+  inputBuffer.insert(inputBufferPosition, 1, c);
+  inputBufferPosition++;
+}
+
+void
+Console::backspace()
+{
+  if ((inputBufferPosition > 0) && (inputBuffer.length() > 0)) {
+    inputBuffer.erase(inputBufferPosition-1, 1);
+    inputBufferPosition--;
+  }
+}
+
+void
+Console::eraseChar()
+{
+  if (inputBufferPosition < (int)inputBuffer.length()) {
+    inputBuffer.erase(inputBufferPosition, 1);
+  }
+}
+
+void
+Console::enter()
+{
+  addLines("> "+inputBuffer);
+  parse(inputBuffer);
+  inputBuffer = "";
+  inputBufferPosition = 0;
+}
+
+void
+Console::scroll(int numLines)
+{
+  offset += numLines;
+  if (offset > 0) offset = 0;
+}
+
+void
+Console::show_history(int offset)
+{
+  while ((offset > 0) && (history_position != history.end())) {
+    history_position++;
+    offset--;
+  }
+  while ((offset < 0) && (history_position != history.begin())) {
+    history_position--;
+    offset++;
+  }
+  if (history_position == history.end()) {
+    inputBuffer = "";
+    inputBufferPosition = 0;
+  } else {
+    inputBuffer = *history_position;
+    inputBufferPosition = inputBuffer.length();
+  }
+}
+
+void 
+Console::move_cursor(int offset)
+{
+  if (offset == -65535) inputBufferPosition = 0;
+  if (offset == +65535) inputBufferPosition = inputBuffer.length();
+  inputBufferPosition+=offset;
+  if (inputBufferPosition < 0) inputBufferPosition = 0;
+  if (inputBufferPosition > (int)inputBuffer.length()) inputBufferPosition = inputBuffer.length();
+}
+
+// Helper functions for Console::autocomplete
+// TODO: Fix rough documentation
+namespace {
+
+void sq_insert_commands(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix);
+
+/**
+ * Acts upon key,value on top of stack:
+ * Appends key (plus type-dependent suffix) to cmds if table_prefix+key starts with search_prefix;
+ * Calls sq_insert_commands if search_prefix starts with table_prefix+key (and value is a table/class/instance);
+ */
+void
+sq_insert_command(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix)
+{
+  const SQChar* key_chars;
+  if (SQ_FAILED(sq_getstring(vm, -2, &key_chars))) return;
+  std::string key_string = table_prefix + key_chars;
+
+  switch (sq_gettype(vm, -1)) {
+    case OT_INSTANCE:
+      key_string+=".";
+      if (search_prefix.substr(0, key_string.length()) == key_string) {
+        sq_getclass(vm, -1);
+       sq_insert_commands(cmds, vm, key_string, search_prefix);
+        sq_pop(vm, 1);
+      }
+      break;
+    case OT_TABLE:
+    case OT_CLASS:
+      key_string+=".";
+      if (search_prefix.substr(0, key_string.length()) == key_string) {
+       sq_insert_commands(cmds, vm, key_string, search_prefix);
+      }
+      break;
+    case OT_CLOSURE:
+    case OT_NATIVECLOSURE:
+      key_string+="()";
+      break;
+    default:
+      break;
+  }
+
+  if (key_string.substr(0, search_prefix.length()) == search_prefix) {
+    cmds.push_back(key_string);
+  }
+
+}
+
+/**
+ * calls sq_insert_command for all entries of table/class on top of stack
+ */
+void
+sq_insert_commands(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix)
+{
+  sq_pushnull(vm); // push iterator
+  while (SQ_SUCCEEDED(sq_next(vm,-2))) {
+    sq_insert_command(cmds, vm, table_prefix, search_prefix);
+    sq_pop(vm, 2); // pop key, val
+  }
+  sq_pop(vm, 1); // pop iterator
+}
+
+
+}
+// End of Console::autocomplete helper functions
+
+void
+Console::autocomplete()
+{
+  //int autocompleteFrom = inputBuffer.find_last_of(" ();+", inputBufferPosition);
+  int autocompleteFrom = inputBuffer.find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_->.", inputBufferPosition);
+  if (autocompleteFrom != (int)std::string::npos) {
+    autocompleteFrom += 1;
+  } else {
+    autocompleteFrom = 0;
+  }
+  std::string prefix = inputBuffer.substr(autocompleteFrom, inputBufferPosition - autocompleteFrom);
+  addLines("> "+prefix);
+
+  std::list<std::string> cmds;
+
+  ready_vm();
+
+  // append all keys of the current root table to list
+  sq_pushroottable(vm); // push root table
+  while(true) {
+    // check all keys (and their children) for matches
+    sq_insert_commands(cmds, vm, "", prefix);
+
+    // cycle through parent(delegate) table
+    SQInteger oldtop = sq_gettop(vm);
+    if(SQ_FAILED(sq_getdelegate(vm, -1)) || oldtop == sq_gettop(vm)) {
+      break;
+    }
+    sq_remove(vm, -2); // remove old table
+  }
+  sq_pop(vm, 1); // remove table
+
+  // depending on number of hits, show matches or autocomplete
+  if (cmds.size() == 0) addLines("No known command starts with \""+prefix+"\"");
+  if (cmds.size() == 1) {
+    // one match: just replace input buffer with full command
+    std::string replaceWith = cmds.front();
+    inputBuffer.replace(autocompleteFrom, prefix.length(), replaceWith);
+    inputBufferPosition += (replaceWith.length() - prefix.length());
+  }
+  if (cmds.size() > 1) {
+    // multiple matches: show all matches and set input buffer to longest common prefix
+    std::string commonPrefix = cmds.front();
+    while (cmds.begin() != cmds.end()) {
+      std::string cmd = cmds.front();
+      cmds.pop_front();
+      addLines(cmd);
+      for (int n = commonPrefix.length(); n >= 1; n--) {
+        if (cmd.compare(0, n, commonPrefix) != 0) commonPrefix.resize(n-1); else break;
+      }
+    }
+    std::string replaceWith = commonPrefix;
+    inputBuffer.replace(autocompleteFrom, prefix.length(), replaceWith);
+    inputBufferPosition += (replaceWith.length() - prefix.length());
+  }
+}
+
+void
+Console::addLines(std::string s)
+{
+  std::istringstream iss(s);
+  std::string line;
+  while (std::getline(iss, line, '\n')) addLine(line);
+}
+
+void
+Console::addLine(std::string s)
+{
+  // output line to stderr
+  std::cerr << s << std::endl;
+
+  // wrap long lines
+  std::string overflow;
+  unsigned int line_count = 0;
+  do {
+    lines.push_front(Font::wrap_to_chars(s, 99, &overflow));
+    line_count++;
+    s = overflow;
+  } while (s.length() > 0);
+
+  // trim scrollback buffer
+  while (lines.size() >= 1000)
+    lines.pop_back();
+
+  // increase console height if necessary
+  if (height < 64) {
+    if(height < 4)
+      height = 4;
+    height += fontheight * line_count;
+  }
+
+  // reset console to full opacity
+  alpha = 1.0;
+
+  // increase time that console stays open
+  if(stayOpen < 6)
+    stayOpen += 1.5;
+}
+
+void
+Console::parse(std::string s)
+{
+  // make sure we actually have something to parse
+  if (s.length() == 0) return;
+
+  // add line to history
+  history.push_back(s);
+  history_position = history.end();
+
+  // split line into list of args
+  std::vector<std::string> args;
+  size_t start = 0;
+  size_t end = 0;
+  while (1) {
+    start = s.find_first_not_of(" ,", end);
+    end = s.find_first_of(" ,", start);
+    if (start == s.npos) break;
+    args.push_back(s.substr(start, end-start));
+  }
+
+  // command is args[0]
+  if (args.size() == 0) return;
+  std::string command = args.front();
+  args.erase(args.begin());
+
+  // ignore if it's an internal command
+  if (consoleCommand(command,args)) return;
+
+  try {
+    execute_script(s);
+  } catch(std::exception& e) {
+    addLines(e.what());
+  }
+
+}
+
+bool
+Console::consoleCommand(std::string /*command*/, std::vector<std::string> /*arguments*/)
+{
+  return false;
+}
+
+bool
+Console::hasFocus()
+{
+  return focused;
+}
+
+void
+Console::show()
+{
+  if(!config->console_enabled)
+    return;
+
+  focused = true;
+  height = 256;
+  alpha = 1.0;
+  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+}
+
+void
+Console::hide()
+{
+  focused = false;
+  height = 0;
+  stayOpen = 0;
+
+  // clear input buffer
+  inputBuffer = "";
+  inputBufferPosition = 0;
+  SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
+}
+
+void
+Console::toggle()
+{
+  if (Console::hasFocus()) {
+    Console::hide();
+  }
+  else {
+    Console::show();
+  }
+}
+
+void
+Console::update(float elapsed_time)
+{
+  if(stayOpen > 0) {
+    stayOpen -= elapsed_time;
+    if(stayOpen < 0)
+      stayOpen = 0;
+  } else if(!focused && height > 0) {
+    alpha -= elapsed_time * FADE_SPEED;
+    if(alpha < 0) {
+      alpha = 0;
+      height = 0;
+    }
+  }
+}
+
+void
+Console::draw(DrawingContext& context)
+{
+  if (height == 0)
+    return;
+
+  int layer = LAYER_GUI + 1;
+
+  context.push_transform();
+  context.set_alpha(alpha);
+  context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 - background->get_width() + backgroundOffset, height - background->get_height()), layer);
+  context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 + backgroundOffset, height - background->get_height()), layer);
+  for (int x = (SCREEN_WIDTH/2 - background->get_width()/2 - (static_cast<int>(ceilf((float)SCREEN_WIDTH / (float)background->get_width()) - 1) * background->get_width())); x < SCREEN_WIDTH; x+=background->get_width()) {
+    context.draw_surface(background.get(), Vector(x, height - background->get_height()), layer);
+  }
+  backgroundOffset+=10;
+  if (backgroundOffset > (int)background->get_width()) backgroundOffset -= (int)background->get_width();
+
+  int lineNo = 0;
+
+  if (focused) {
+    lineNo++;
+    float py = height-4-1 * font->get_height();
+    context.draw_text(font.get(), "> "+inputBuffer, Vector(4, py), ALIGN_LEFT, layer);
+    if (SDL_GetTicks() % 1000 < 750) {
+      int cursor_px = 2 + inputBufferPosition;
+      context.draw_text(font.get(), "_", Vector(4 + (cursor_px * font->get_text_width("X")), py), ALIGN_LEFT, layer);
+    }
+  }
+
+  int skipLines = -offset;
+  for (std::list<std::string>::iterator i = lines.begin(); i != lines.end(); i++) {
+    if (skipLines-- > 0) continue;
+    lineNo++;
+    float py = height - 4 - lineNo*font->get_height();
+    if (py < -font->get_height()) break;
+    context.draw_text(font.get(), *i, Vector(4, py), ALIGN_LEFT, layer);
+  }
+  context.pop_transform();
+}
+
+Console* Console::instance = NULL;
+int Console::inputBufferPosition = 0;
+std::string Console::inputBuffer;
+ConsoleStreamBuffer Console::outputBuffer;
+std::ostream Console::output(&Console::outputBuffer);
+
diff --git a/src/console.hpp b/src/console.hpp
new file mode 100644 (file)
index 0000000..59d3934
--- /dev/null
@@ -0,0 +1,141 @@
+//  $Id$
+//
+//  SuperTux - Console
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_CONSOLE_H
+#define SUPERTUX_CONSOLE_H
+
+#include <list>
+#include <map>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <squirrel.h>
+
+class Console;
+class ConsoleStreamBuffer;
+class ConsoleCommandReceiver;
+class DrawingContext;
+class Surface;
+class Font;
+
+class Console
+{
+public:
+  Console();
+  ~Console();
+
+  static Console* instance;
+
+  static std::ostream output; /**< stream of characters to output to the console. Do not forget to send std::endl or to flush the stream. */
+
+  void init_graphics();
+
+  void input(char c); /**< add character to inputBuffer */
+  void backspace(); /**< delete character left of inputBufferPosition */
+  void eraseChar(); /**< delete character at inputBufferPosition */
+  void enter(); /**< process and clear input stream */
+  void scroll(int offset); /**< scroll console text up or down by @c offset lines */
+  void autocomplete(); /**< autocomplete current command */
+  void show_history(int offset); /**< move @c offset lines forward through history; Negative offset moves backward */
+  void move_cursor(int offset); /**< move the cursor @c offset chars to the right; Negative offset moves backward; 0xFFFF moves to the end */
+
+  void draw(DrawingContext& context); /**< draw the console in a DrawingContext */
+  void update(float elapsed_time);
+
+  void show(); /**< display the console */
+  void hide(); /**< hide the console */
+  void toggle(); /**< display the console if hidden, hide otherwise */
+
+  bool hasFocus(); /**< true if characters should be sent to the console instead of their normal target */
+
+  template<typename T> static bool string_is(std::string s) {
+    std::istringstream iss(s);
+    T i;
+    if ((iss >> i) && iss.eof()) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  template<typename T> static T string_to(std::string s) {
+    std::istringstream iss(s);
+    T i;
+    if ((iss >> i) && iss.eof()) {
+      return i;
+    } else {
+      return T();
+    }
+  }
+
+private:
+  std::list<std::string> history; /**< command history. New lines get added to back. */
+  std::list<std::string>::iterator history_position; /**< item of command history that is currently displayed */
+  std::list<std::string> lines; /**< backbuffer of lines sent to the console. New lines get added to front. */
+
+  std::auto_ptr<Surface> background; /**< console background image */
+  std::auto_ptr<Surface> background2; /**< second, moving console background image */
+
+  HSQUIRRELVM vm; /**< squirrel thread for the console (with custom roottable) */
+  HSQOBJECT vm_object;
+
+  int backgroundOffset; /**< current offset of scrolling background image */
+  float height; /**< height of the console in px */
+  float alpha;
+  int offset; /**< decrease to scroll text up */
+  bool focused; /**< true if console has input focus */
+  std::auto_ptr<Font> font;
+  float fontheight; /**< height of the font (this is a separate var, because the font could not be initialized yet but is needed in the addLine message */
+
+  float stayOpen;
+
+  static int inputBufferPosition; /**< position in inputBuffer before which to append new characters */
+  static std::string inputBuffer; /**< string used for keyboard input */
+  static ConsoleStreamBuffer outputBuffer; /**< stream buffer used by output stream */
+
+  void addLines(std::string s); /**< display a string of (potentially) multiple lines in the console */
+  void addLine(std::string s); /**< display a line in the console */
+  void parse(std::string s); /**< react to a given command */
+
+  /** ready a virtual machine instance, creating a new thread and loading default .nut files if needed */
+  void ready_vm();
+
+  /** execute squirrel script and output result */
+  void execute_script(const std::string& s);
+
+  bool consoleCommand(std::string command, std::vector<std::string> arguments); /**< process internal command; return false if command was unknown, true otherwise */
+
+  friend class ConsoleStreamBuffer;
+  void flush(ConsoleStreamBuffer* buffer); /**< act upon changes in a ConsoleStreamBuffer */
+};
+
+class ConsoleStreamBuffer : public std::stringbuf
+{
+  public:
+    int sync()
+    {
+      int result = std::stringbuf::sync();
+      if(Console::instance != NULL)
+        Console::instance->flush(this);
+      return result;
+    }
+};
+
+#endif
diff --git a/src/control/codecontroller.cpp b/src/control/codecontroller.cpp
new file mode 100644 (file)
index 0000000..81bbfeb
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "codecontroller.hpp"
+
+CodeController::CodeController()
+{}
+
+CodeController::~CodeController()
+{}
+
+void
+CodeController::press(Control c, bool pressed)
+{
+  controls[c] = pressed;
+}
+
+void
+CodeController::update()
+{
+  Controller::update();
+
+  for(int i = 0; i < CONTROLCOUNT; ++i)
+    controls[i] = false;
+}
diff --git a/src/control/codecontroller.hpp b/src/control/codecontroller.hpp
new file mode 100644 (file)
index 0000000..fdc0893
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __CODECONTROLLER_H__
+#define __CODECONTROLLER_H__
+
+#include "controller.hpp"
+
+/**
+ * This is a dummy controler that doesn't react to any user input but should
+ * be controlled by code
+ */
+class CodeController : public Controller
+{
+public:
+  CodeController();
+  virtual ~CodeController();
+
+  void press(Control c, bool pressed = true);
+  void update();
+};
+
+#endif
diff --git a/src/control/controller.cpp b/src/control/controller.cpp
new file mode 100644 (file)
index 0000000..ee7a3e2
--- /dev/null
@@ -0,0 +1,79 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "controller.hpp"
+
+const char* Controller::controlNames[] = {
+  "left",
+  "right",
+  "up",
+  "down",
+  "jump",
+  "action",
+  "pause-menu",
+  "menu-select",
+  "console",
+  "peek-left",
+  "peek-right",
+  0
+};
+
+Controller::Controller()
+{
+  reset();
+}
+
+Controller::~Controller()
+{}
+
+void
+Controller::reset()
+{
+  for(int i = 0; i < CONTROLCOUNT; ++i) {
+    controls[i] = false;
+    oldControls[i] = false;
+  }
+}
+
+bool
+Controller::hold(Control control)
+{
+  return controls[control];
+}
+
+bool
+Controller::pressed(Control control)
+{
+  return !oldControls[control] && controls[control];
+}
+
+bool
+Controller::released(Control control)
+{
+  return oldControls[control] && !controls[control];
+}
+
+void
+Controller::update()
+{
+  for(int i = 0; i < CONTROLCOUNT; ++i)
+    oldControls[i] = controls[i];
+}
diff --git a/src/control/controller.hpp b/src/control/controller.hpp
new file mode 100644 (file)
index 0000000..c8b45e8
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __CONTROLLER_H__
+#define __CONTROLLER_H__
+
+class Controller
+{
+public:
+  static const char* controlNames[];
+
+  enum Control {
+    LEFT = 0,
+    RIGHT,
+    UP,
+    DOWN,
+
+    JUMP,
+    ACTION,
+
+    PAUSE_MENU,
+    MENU_SELECT,
+
+    CONSOLE,
+
+    PEEK_LEFT,
+    PEEK_RIGHT,
+
+    CONTROLCOUNT
+  };
+
+  Controller();
+  virtual ~Controller();
+
+  /** returns true if the control is pressed down */
+  bool hold(Control control);
+  /** returns true if the control has just been pressed down this frame */
+  bool pressed(Control control);
+  /** returns true if the control has just been released this frame */
+  bool released(Control control);
+
+  virtual void reset();
+  virtual void update();
+
+protected:
+  /** current control status */
+  bool controls[CONTROLCOUNT];
+  /** control status at last frame */
+  bool oldControls[CONTROLCOUNT];
+};
+
+#endif
diff --git a/src/control/joystickkeyboardcontroller.cpp b/src/control/joystickkeyboardcontroller.cpp
new file mode 100644 (file)
index 0000000..2bc549d
--- /dev/null
@@ -0,0 +1,1000 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
+//                2007 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <sstream>
+#include "joystickkeyboardcontroller.hpp"
+#include "log.hpp"
+#include "gui/menu.hpp"
+#include "gettext.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "game_session.hpp"
+#include "console.hpp"
+#include "gameconfig.hpp"
+
+class JoystickKeyboardController::JoystickMenu : public Menu
+{
+public:
+  JoystickMenu(JoystickKeyboardController* controller);
+  virtual ~JoystickMenu();
+
+  void update();
+  std::string get_button_name(int button);
+  void update_menu_item(Control id);
+  virtual void menu_action(MenuItem* item);
+  JoystickKeyboardController* controller;
+};
+
+class JoystickKeyboardController::KeyboardMenu : public Menu
+{
+public:
+  KeyboardMenu(JoystickKeyboardController* controller);
+  ~KeyboardMenu();
+
+  void update();
+  std::string get_key_name(SDLKey key);
+  virtual void menu_action(MenuItem* item);
+  JoystickKeyboardController* controller;
+};
+
+JoystickKeyboardController::JoystickKeyboardController()
+  : hat_state(0),
+    wait_for_key(-1), wait_for_joystick(-1),
+    key_options_menu(0), joystick_options_menu(0)
+{
+  // initialize default keyboard map
+  keymap[SDLK_LEFT]     = LEFT;
+  keymap[SDLK_RIGHT]    = RIGHT;
+  keymap[SDLK_UP]       = UP;
+  keymap[SDLK_DOWN]     = DOWN;
+  keymap[SDLK_SPACE]    = JUMP;
+  keymap[SDLK_LCTRL]    = ACTION;
+  keymap[SDLK_LALT]     = ACTION;
+  keymap[SDLK_ESCAPE]   = PAUSE_MENU;
+  keymap[SDLK_p]        = PAUSE_MENU;
+  keymap[SDLK_PAUSE]    = PAUSE_MENU;
+  keymap[SDLK_RETURN]   = MENU_SELECT;
+  keymap[SDLK_KP_ENTER] = MENU_SELECT;
+  keymap[SDLK_CARET]    = CONSOLE;
+  keymap[SDLK_DELETE]   = PEEK_LEFT;
+  keymap[SDLK_END]      = PEEK_RIGHT;
+
+  jump_with_up = false;
+
+  int joystick_count = SDL_NumJoysticks();
+  min_joybuttons = -1;
+  max_joybuttons = -1;
+  max_joyaxis    = -1;
+  max_joyhats    = -1;
+
+  for(int i = 0; i < joystick_count; ++i) {
+    SDL_Joystick* joystick = SDL_JoystickOpen(i);
+    bool good = true;
+    if(SDL_JoystickNumButtons(joystick) < 2) {
+      log_info << "Joystick " << i << " has less than 2 buttons" << std::endl;
+      good = false;
+    }
+    if(SDL_JoystickNumAxes(joystick) < 2
+       && SDL_JoystickNumHats(joystick) == 0) {
+      log_info << "Joystick " << i << " has less than 2 axes and no hat" << std::endl;
+      good = false;
+    }
+    if(!good) {
+      SDL_JoystickClose(joystick);
+      continue;
+    }
+
+    if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
+      min_joybuttons = SDL_JoystickNumButtons(joystick);
+
+    if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
+      max_joybuttons = SDL_JoystickNumButtons(joystick);
+
+    if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
+      max_joyaxis = SDL_JoystickNumAxes(joystick);
+
+    if(SDL_JoystickNumHats(joystick) > max_joyhats)
+      max_joyhats = SDL_JoystickNumHats(joystick);
+
+    joysticks.push_back(joystick);
+  }
+
+  dead_zone = 1000;
+
+  // Default joystick button configuration
+  joy_button_map[0] = JUMP;
+  joy_button_map[1] = ACTION;
+  // 6 or more Buttons
+  if( min_joybuttons > 5 ){
+    joy_button_map[4] = PEEK_LEFT;
+    joy_button_map[5] = PEEK_RIGHT;
+    // 8 or more
+    if(min_joybuttons > 7)
+      joy_button_map[min_joybuttons-1] = PAUSE_MENU;
+  } else {
+    // map the last 2 buttons to menu and pause
+    if(min_joybuttons > 2)
+      joy_button_map[min_joybuttons-1] = PAUSE_MENU;
+    // map all remaining joystick buttons to MENU_SELECT
+    for(int i = 2; i < max_joybuttons; ++i) {
+      if(i != min_joybuttons-1)
+        joy_button_map[i] = MENU_SELECT;
+    }
+  }
+
+  // Default joystick axis configuration
+  joy_axis_map[-1] = LEFT;
+  joy_axis_map[ 1] = RIGHT;
+  joy_axis_map[-2] = UP;
+  joy_axis_map[ 2] = DOWN;
+
+  // some joysticks or SDL seem to produce some bogus events after being opened
+  Uint32 ticks = SDL_GetTicks();
+  while(SDL_GetTicks() - ticks < 200) {
+    SDL_Event event;
+    SDL_PollEvent(&event);
+  }
+}
+
+JoystickKeyboardController::~JoystickKeyboardController()
+{
+  for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
+      i != joysticks.end(); ++i) {
+    if(*i != 0)
+      SDL_JoystickClose(*i);
+  }
+
+  delete key_options_menu;
+  delete joystick_options_menu;
+}
+
+void
+JoystickKeyboardController::read(const lisp::Lisp& lisp)
+{
+  const lisp::Lisp* keymap_lisp = lisp.get_lisp("keymap");
+  if(keymap_lisp) {
+    keymap.clear();
+    lisp::ListIterator iter(keymap_lisp);
+    while(iter.next()) {
+      if(iter.item() == "map") {
+        int key = -1;
+        std::string control;
+        const lisp::Lisp* map = iter.lisp();
+        map->get("key", key);
+        map->get("control", control);
+        if(key < SDLK_FIRST || key >= SDLK_LAST) {
+          log_info << "Invalid key '" << key << "' in keymap" << std::endl;
+          continue;
+        }
+
+        int i = 0;
+        for(i = 0; controlNames[i] != 0; ++i) {
+          if(control == controlNames[i])
+            break;
+        }
+        if(controlNames[i] == 0) {
+          log_info << "Invalid control '" << control << "' in keymap" << std::endl;
+          continue;
+        }
+        keymap[(SDLKey) key] = (Control)i;
+      } else {
+        log_info << "Invalid lisp element '" << iter.item() << "' in keymap" << std::endl;
+      }
+    }
+  }
+
+  const lisp::Lisp* joystick_lisp = lisp.get_lisp("joystick");
+  if(joystick_lisp) {
+    joystick_lisp->get("dead-zone", dead_zone);
+    joystick_lisp->get("jump-with-up", jump_with_up);
+    lisp::ListIterator iter(joystick_lisp);
+    while(iter.next()) {
+      if(iter.item() == "map") {
+        int button = -1;
+        int axis   = 0;
+        int hat    = -1;
+        std::string control;
+        const lisp::Lisp* map = iter.lisp();
+
+        map->get("control", control);
+        int i = 0;
+        for(i = 0; controlNames[i] != 0; ++i) {
+          if(control == controlNames[i])
+            break;
+        }
+        if(controlNames[i] == 0) {
+          log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
+          continue;
+        }
+
+        if (map->get("button", button)) {
+          if(button < 0 || button >= max_joybuttons) {
+            log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
+            continue;
+          }
+          bind_joybutton(button, (Control) i);
+        }
+
+        if (map->get("axis",   axis)) {
+          if (axis == 0 || abs(axis) > max_joyaxis) {
+            log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
+            continue;
+          }
+          bind_joyaxis(axis, (Control) i);
+        }
+
+        if (map->get("hat",   hat)) {
+          if (hat != SDL_HAT_UP   &&
+              hat != SDL_HAT_DOWN &&
+              hat != SDL_HAT_LEFT &&
+              hat != SDL_HAT_RIGHT) {
+            log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
+            continue;
+          } else {
+            bind_joyhat(hat, (Control) i);
+          }
+        }
+      }
+    }
+  }
+}
+
+void
+JoystickKeyboardController::write(lisp::Writer& writer)
+{
+  writer.start_list("keymap");
+  for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
+    writer.start_list("map");
+    writer.write_int("key", (int) i->first);
+    writer.write_string("control", controlNames[i->second]);
+    writer.end_list("map");
+  }
+  writer.end_list("keymap");
+
+  writer.start_list("joystick");
+  writer.write_int("dead-zone", dead_zone);
+  writer.write_bool("jump-with-up", jump_with_up);
+
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
+      ++i) {
+    writer.start_list("map");
+    writer.write_int("button", i->first);
+    writer.write_string("control", controlNames[i->second]);
+    writer.end_list("map");
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    writer.start_list("map");
+    writer.write_int("hat", i->first);
+    writer.write_string("control", controlNames[i->second]);
+    writer.end_list("map");
+  }
+
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+    writer.start_list("map");
+    writer.write_int("axis", i->first);
+    writer.write_string("control", controlNames[i->second]);
+    writer.end_list("map");
+  }
+
+  writer.end_list("joystick");
+}
+
+void
+JoystickKeyboardController::reset()
+{
+  Controller::reset();
+}
+
+void
+JoystickKeyboardController::set_joy_controls(Control id, bool value)
+{
+  if (jump_with_up && id == Controller::UP)
+    controls[Controller::JUMP] = value;
+
+  controls[(Control)id] = value;
+}
+
+void
+JoystickKeyboardController::process_event(const SDL_Event& event)
+{
+  switch(event.type) {
+    case SDL_KEYUP:
+    case SDL_KEYDOWN:
+      process_key_event(event);
+      break;
+
+    case SDL_JOYAXISMOTION:
+      process_axis_event(event.jaxis);
+      break;
+
+    case SDL_JOYHATMOTION:
+      process_hat_event(event.jhat);
+      break;
+
+    case SDL_JOYBUTTONDOWN:
+    case SDL_JOYBUTTONUP:
+      process_button_event(event.jbutton);
+      break;
+
+    default:
+      break;
+  }
+}
+
+void
+JoystickKeyboardController::process_button_event(const SDL_JoyButtonEvent& jbutton)
+{
+  if(wait_for_joystick >= 0) 
+    {
+      if(jbutton.state == SDL_PRESSED)
+        {
+          bind_joybutton(jbutton.button, (Control)wait_for_joystick);
+          joystick_options_menu->update();
+          reset();
+          wait_for_joystick = -1;
+        }
+    } 
+  else 
+    {
+      ButtonMap::iterator i = joy_button_map.find(jbutton.button);
+      if(i == joy_button_map.end()) {
+        log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
+      } else {
+        set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
+      }
+    }
+}
+
+void
+JoystickKeyboardController::process_axis_event(const SDL_JoyAxisEvent& jaxis)
+{
+  if (wait_for_joystick >= 0)
+    {
+      if (abs(jaxis.value) > dead_zone) {
+        if (jaxis.value < 0)
+          bind_joyaxis(-(jaxis.axis + 1), Control(wait_for_joystick));
+        else
+          bind_joyaxis(jaxis.axis + 1, Control(wait_for_joystick));
+
+        joystick_options_menu->update();
+        wait_for_joystick = -1;
+      }
+    }
+  else
+    {
+      // Split the axis into left and right, so that both can be
+      // mapped seperatly (needed for jump/down vs up/down)
+      int axis = jaxis.axis + 1;
+
+      AxisMap::iterator left  = joy_axis_map.find(-axis);
+      AxisMap::iterator right = joy_axis_map.find(axis);
+
+      if(left == joy_axis_map.end()) {
+        std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+      } else {
+        if (jaxis.value < -dead_zone)
+          set_joy_controls(left->second,  true);
+        else if (jaxis.value > dead_zone)
+          set_joy_controls(left->second, false);
+        else
+          set_joy_controls(left->second, false);
+      }
+
+      if(right == joy_axis_map.end()) {
+        std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+      } else {
+        if (jaxis.value < -dead_zone)
+          set_joy_controls(right->second, false);
+        else if (jaxis.value > dead_zone)
+          set_joy_controls(right->second, true);
+        else
+          set_joy_controls(right->second, false);
+      }
+    }
+}
+
+void
+JoystickKeyboardController::process_hat_event(const SDL_JoyHatEvent& jhat)
+{
+  Uint8 changed = hat_state ^ jhat.value;
+
+  if (wait_for_joystick >= 0)
+    {
+      if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
+        bind_joyhat(SDL_HAT_UP, (Control)wait_for_joystick);
+
+      if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
+        bind_joyhat(SDL_HAT_DOWN, (Control)wait_for_joystick);
+
+      if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
+        bind_joyhat(SDL_HAT_LEFT, (Control)wait_for_joystick);
+
+      if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
+        bind_joyhat(SDL_HAT_RIGHT, (Control)wait_for_joystick);
+
+      joystick_options_menu->update();
+      wait_for_joystick = -1;
+    }
+  else
+    {
+      if (changed & SDL_HAT_UP)
+        {
+          HatMap::iterator it = joy_hat_map.find(SDL_HAT_UP);
+          if (it != joy_hat_map.end())
+            set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
+        }
+
+      if (changed & SDL_HAT_DOWN)
+        {
+          HatMap::iterator it = joy_hat_map.find(SDL_HAT_DOWN);
+          if (it != joy_hat_map.end())
+            set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
+        }
+
+      if (changed & SDL_HAT_LEFT)
+        {
+          HatMap::iterator it = joy_hat_map.find(SDL_HAT_LEFT);
+          if (it != joy_hat_map.end())
+            set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
+        }
+
+      if (changed & SDL_HAT_RIGHT)
+        {
+          HatMap::iterator it = joy_hat_map.find(SDL_HAT_RIGHT);
+          if (it != joy_hat_map.end())
+            set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
+        }
+    }
+
+  hat_state = jhat.value;
+}
+
+void
+JoystickKeyboardController::process_key_event(const SDL_Event& event)
+{
+  KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
+
+  // if console key was pressed: toggle console
+  if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
+    if (event.type == SDL_KEYDOWN) 
+      Console::instance->toggle();
+  } else {
+    if (Console::instance->hasFocus()) {
+      // if console is open: send key there
+      process_console_key_event(event);
+    } else if (Menu::current()) {
+      // if menu mode: send key there
+      process_menu_key_event(event);
+    } else if(key_mapping == keymap.end()) {
+      // default action: update controls
+      log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
+    } else {
+      Control control = key_mapping->second;
+      controls[control] = (event.type == SDL_KEYDOWN);
+    }
+  }
+}
+
+void
+JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
+{
+  if (event.type != SDL_KEYDOWN) return;
+
+  switch (event.key.keysym.sym) {
+    case SDLK_RETURN:
+      Console::instance->enter();
+      break;
+    case SDLK_BACKSPACE:
+      Console::instance->backspace();
+      break;
+    case SDLK_TAB:
+      Console::instance->autocomplete();
+      break;
+    case SDLK_PAGEUP:
+      Console::instance->scroll(-1);
+      break;
+    case SDLK_PAGEDOWN:
+      Console::instance->scroll(+1);
+      break;
+    case SDLK_HOME:
+      Console::instance->move_cursor(-65535);
+      break;
+    case SDLK_END:
+      Console::instance->move_cursor(+65535);
+      break;
+    case SDLK_UP:
+      Console::instance->show_history(-1);
+      break;
+    case SDLK_DOWN:
+      Console::instance->show_history(+1);
+      break;
+    case SDLK_LEFT:
+      Console::instance->move_cursor(-1);
+      break;
+    case SDLK_RIGHT:
+      Console::instance->move_cursor(+1);
+      break;
+    default:
+      int c = event.key.keysym.unicode;
+      if ((c >= 32) && (c <= 126)) {
+       Console::instance->input((char)c);
+      }
+      break;
+  }
+}
+
+void
+JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
+{
+  // wait for key mode?
+  if(wait_for_key >= 0) {
+    if(event.type == SDL_KEYUP)
+      return;
+
+    if(event.key.keysym.sym != SDLK_ESCAPE
+        && event.key.keysym.sym != SDLK_PAUSE) {
+      bind_key(event.key.keysym.sym, (Control) wait_for_key);
+    }
+    reset();
+    key_options_menu->update();
+    wait_for_key = -1;
+    return;
+  }
+  if(wait_for_joystick >= 0) {
+    if(event.key.keysym.sym == SDLK_ESCAPE) {
+      reset();
+      joystick_options_menu->update();
+      wait_for_joystick = -1;
+    }
+    return;
+  }
+
+  Control control;
+  /* we use default keys when the menu is open (to avoid problems when
+   * redefining keys to invalid settings
+   */
+  switch(event.key.keysym.sym) {
+    case SDLK_UP:
+      control = UP;
+      break;
+    case SDLK_DOWN:
+      control = DOWN;
+      break;
+    case SDLK_LEFT:
+      control = LEFT;
+      break;
+    case SDLK_RIGHT:
+      control = RIGHT;
+      break;
+    case SDLK_SPACE:
+    case SDLK_RETURN:
+    case SDLK_KP_ENTER:
+      control = MENU_SELECT;
+      break;
+    case SDLK_ESCAPE:
+    case SDLK_PAUSE:
+      control = PAUSE_MENU;
+      break;
+    default:
+      return;
+      break;
+  }
+
+  controls[control] = (event.type == SDL_KEYDOWN);
+}
+
+void
+JoystickKeyboardController::unbind_joystick_control(Control control)
+{
+  // remove all previous mappings for that control
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_axis_map.erase(i++);
+    else
+      ++i;
+  }
+
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_button_map.erase(i++);
+    else
+      ++i;
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin();  i != joy_hat_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_hat_map.erase(i++);
+    else
+      ++i;
+  }
+}
+
+void
+JoystickKeyboardController::bind_joyaxis(int axis, Control control)
+{
+  // axis isn't the SDL axis number, but axisnumber + 1 with sign
+  // changed depending on if the positive or negative end is to be
+  // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
+  // etc.)
+
+  unbind_joystick_control(control);
+
+  // add new mapping
+  joy_axis_map[axis] = control;
+}
+
+void
+JoystickKeyboardController::bind_joyhat(int dir, Control c)
+{
+  unbind_joystick_control(c);
+
+  // add new mapping
+  joy_hat_map[dir] = c;
+}
+
+void
+JoystickKeyboardController::bind_joybutton(int button, Control control)
+{
+  unbind_joystick_control(control);
+
+  // add new mapping
+  joy_button_map[button] = control;
+}
+
+void
+JoystickKeyboardController::bind_key(SDLKey key, Control control)
+{
+  // remove all previous mappings for that control and for that key
+  for(KeyMap::iterator i = keymap.begin();
+      i != keymap.end(); /* no ++i */) {
+    if(i->second == control) {
+      KeyMap::iterator e = i;
+      ++i;
+      keymap.erase(e);
+    } else {
+      ++i;
+    }
+  }
+
+  KeyMap::iterator i = keymap.find(key);
+  if(i != keymap.end())
+    keymap.erase(i);
+
+  // add new mapping
+  keymap[key]= control;
+}
+
+void
+JoystickKeyboardController::print_joystick_mappings()
+{
+  std::cout << "Joystick Mappings" << std::endl;
+  std::cout << "-----------------" << std::endl;
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+    std::cout << "Axis: " << i->first << " -> " << i->second << std::endl;
+  }
+
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
+    std::cout << "Button: " << i->first << " -> " << i->second << std::endl;
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    std::cout << "Hat: " << i->first << " -> " << i->second << std::endl;
+  }
+  std::cout << std::endl;
+}
+
+SDLKey
+JoystickKeyboardController::reversemap_key(Control c)
+{
+  for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
+    if(i->second == c)
+      return i->first;
+  }
+
+  return SDLK_UNKNOWN;
+}
+
+int
+JoystickKeyboardController::reversemap_joyaxis(Control c)
+{
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+    if(i->second == c)
+      return i->first;
+  }
+
+  return 0;
+}
+
+int
+JoystickKeyboardController::reversemap_joybutton(Control c)
+{
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
+    if(i->second == c)
+      return i->first;
+  }
+
+  return -1;
+}
+
+int
+JoystickKeyboardController::reversemap_joyhat(Control c)
+{
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    if(i->second == c)
+      return i->first;
+  }
+
+  return -1;
+}
+
+Menu*
+JoystickKeyboardController::get_key_options_menu()
+{
+  if(key_options_menu == 0) {
+    key_options_menu = new KeyboardMenu(this);
+  }
+
+  return key_options_menu;
+}
+
+Menu*
+JoystickKeyboardController::get_joystick_options_menu()
+{
+  if(joystick_options_menu == 0) {
+    joystick_options_menu = new JoystickMenu(this);
+  }
+
+  return joystick_options_menu;
+}
+
+//----------------------------------------------------------------------------
+
+JoystickKeyboardController::KeyboardMenu::KeyboardMenu(
+    JoystickKeyboardController* _controller)
+  : controller(_controller)
+{
+    add_label(_("Setup Keyboard"));
+    add_hl();
+    add_controlfield(Controller::UP,         _("Up"));
+    add_controlfield(Controller::DOWN,       _("Down"));
+    add_controlfield(Controller::LEFT,       _("Left"));
+    add_controlfield(Controller::RIGHT,      _("Right"));
+    add_controlfield(Controller::JUMP,       _("Jump"));
+    add_controlfield(Controller::ACTION,     _("Action"));
+    add_controlfield(Controller::PEEK_LEFT,  _("Peek Left"));
+    add_controlfield(Controller::PEEK_RIGHT, _("Peek Right"));
+    if (config->console_enabled) {
+      add_controlfield(Controller::CONSOLE, _("Console"));
+    }
+    add_hl();
+    add_back(_("Back"));
+    update();
+}
+
+JoystickKeyboardController::KeyboardMenu::~KeyboardMenu()
+{}
+
+std::string
+JoystickKeyboardController::KeyboardMenu::get_key_name(SDLKey key)
+{
+  switch(key) {
+    case SDLK_UNKNOWN:
+      return _("None");
+    case SDLK_UP:
+      return _("Up cursor");
+    case SDLK_DOWN:
+      return _("Down cursor");
+    case SDLK_LEFT:
+      return _("Left cursor");
+    case SDLK_RIGHT:
+      return _("Right cursor");
+    case SDLK_RETURN:
+      return _("Return");
+    case SDLK_SPACE:
+      return _("Space");
+    case SDLK_RSHIFT:
+      return _("Right Shift");
+    case SDLK_LSHIFT:
+      return _("Left Shift");
+    case SDLK_RCTRL:
+      return _("Right Control");
+    case SDLK_LCTRL:
+      return _("Left Control");
+    case SDLK_RALT:
+      return _("Right Alt");
+    case SDLK_LALT:
+      return _("Left Alt");
+    default:
+      return SDL_GetKeyName((SDLKey) key);
+  }
+}
+
+void
+JoystickKeyboardController::KeyboardMenu::menu_action(MenuItem* item)
+{
+  assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
+  item->change_input(_("Press Key"));
+  controller->wait_for_key = item->id;
+}
+
+void
+JoystickKeyboardController::KeyboardMenu::update()
+{
+  // update menu
+  get_item_by_id((int) Controller::UP).change_input(get_key_name(
+    controller->reversemap_key(Controller::UP)));
+  get_item_by_id((int) Controller::DOWN).change_input(get_key_name(
+    controller->reversemap_key(Controller::DOWN)));
+  get_item_by_id((int) Controller::LEFT).change_input(get_key_name(
+    controller->reversemap_key(Controller::LEFT)));
+  get_item_by_id((int) Controller::RIGHT).change_input(get_key_name(
+    controller->reversemap_key(Controller::RIGHT)));
+  get_item_by_id((int) Controller::JUMP).change_input(get_key_name(
+    controller->reversemap_key(Controller::JUMP)));
+  get_item_by_id((int) Controller::ACTION).change_input(get_key_name(
+    controller->reversemap_key(Controller::ACTION)));
+  get_item_by_id((int) Controller::PEEK_LEFT).change_input(get_key_name(
+    controller->reversemap_key(Controller::PEEK_LEFT)));
+  get_item_by_id((int) Controller::PEEK_RIGHT).change_input(get_key_name(
+    controller->reversemap_key(Controller::PEEK_RIGHT)));
+  if (config->console_enabled) {
+    get_item_by_id((int) Controller::CONSOLE).change_input(get_key_name(
+      controller->reversemap_key(Controller::CONSOLE)));
+  }
+}
+
+//---------------------------------------------------------------------------
+
+JoystickKeyboardController::JoystickMenu::JoystickMenu(
+  JoystickKeyboardController* _controller)
+  : controller(_controller)
+{
+  add_label(_("Setup Joystick"));
+  add_hl();
+  if(controller->joysticks.size() > 0) {
+    add_controlfield(Controller::UP,          _("Up"));
+    add_controlfield(Controller::DOWN,        _("Down"));
+    add_controlfield(Controller::LEFT,        _("Left"));
+    add_controlfield(Controller::RIGHT,       _("Right"));
+    add_controlfield(Controller::JUMP,        _("Jump"));
+    add_controlfield(Controller::ACTION,      _("Action"));
+    add_controlfield(Controller::PAUSE_MENU,  _("Pause/Menu"));
+    add_controlfield(Controller::PEEK_LEFT,   _("Peek Left"));
+    add_controlfield(Controller::PEEK_RIGHT,  _("Peek Right"));
+
+    add_toggle(Controller::CONTROLCOUNT, _("Jump with Up"), controller->jump_with_up);
+  } else {
+    add_deactive(-1, _("No Joysticks found"));
+  }
+  add_hl();
+  add_back(_("Back"));
+  update();
+}
+
+JoystickKeyboardController::JoystickMenu::~JoystickMenu()
+{}
+
+std::string
+JoystickKeyboardController::JoystickMenu::get_button_name(int button)
+{
+  if(button < 0)
+    return _("None");
+
+  std::ostringstream name;
+  name << "Button " << button;
+  return name.str();
+}
+
+void
+JoystickKeyboardController::JoystickMenu::menu_action(MenuItem* item)
+{
+  if (item->id >= 0 && item->id < Controller::CONTROLCOUNT) {
+    item->change_input(_("Press Button"));
+    controller->wait_for_joystick = item->id;
+  } else if (item->id == Controller::CONTROLCOUNT) {
+    controller->jump_with_up = item->toggled;
+  }
+}
+
+void
+JoystickKeyboardController::JoystickMenu::update_menu_item(Control id)
+{
+  int button  = controller->reversemap_joybutton(id);
+  int axis    = controller->reversemap_joyaxis(id);
+  int hat_dir = controller->reversemap_joyhat(id);
+
+  if (button != -1) {
+    get_item_by_id((int)id).change_input(get_button_name(button));
+  } else if (axis != 0) {
+    std::ostringstream name;
+
+    name << "Axis ";
+
+    if (axis < 0)
+      name << "-";
+    else
+      name << "+";
+
+    if (abs(axis) == 1)
+      name << "X";
+    else if (abs(axis) == 2)
+      name << "Y";
+    else if (abs(axis) == 2)
+      name << "X2";
+    else if (abs(axis) == 3)
+      name << "Y2";
+    else
+      name << abs(axis);
+
+    get_item_by_id((int)id).change_input(name.str());
+  } else if (hat_dir != -1) {
+    std::string name;
+
+    switch (hat_dir)
+      {
+        case SDL_HAT_UP:
+          name = "Hat Up";
+          break;
+
+        case SDL_HAT_DOWN:
+          name = "Hat Down";
+          break;
+
+        case SDL_HAT_LEFT:
+          name = "Hat Left";
+          break;
+
+        case SDL_HAT_RIGHT:
+          name = "Hat Right";
+          break;
+
+        default:
+          name = "Unknown hat_dir";
+          break;
+      }
+
+    get_item_by_id((int)id).change_input(name);
+  } else {
+    get_item_by_id((int)id).change_input("None");
+  }
+}
+
+void
+JoystickKeyboardController::JoystickMenu::update()
+{
+  if(controller->joysticks.size() == 0)
+    return;
+
+  update_menu_item(Controller::UP);
+  update_menu_item(Controller::DOWN);
+  update_menu_item(Controller::LEFT);
+  update_menu_item(Controller::RIGHT);
+
+  update_menu_item(Controller::JUMP);
+  update_menu_item(Controller::ACTION);
+  update_menu_item(Controller::PAUSE_MENU);
+  update_menu_item(Controller::PEEK_LEFT);
+  update_menu_item(Controller::PEEK_RIGHT);
+
+  get_item_by_id(Controller::CONTROLCOUNT).toggled = controller->jump_with_up;
+}
diff --git a/src/control/joystickkeyboardcontroller.hpp b/src/control/joystickkeyboardcontroller.hpp
new file mode 100644 (file)
index 0000000..50225dc
--- /dev/null
@@ -0,0 +1,115 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __JOYSTICKKEYBOARDCONTROLLER_H__
+#define __JOYSTICKKEYBOARDCONTROLLER_H__
+
+#include "controller.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include <SDL.h>
+#include <string>
+#include <map>
+
+class Menu;
+
+class JoystickKeyboardController : public Controller
+{
+public:
+  JoystickKeyboardController();
+  virtual ~JoystickKeyboardController();
+
+  /** Process an SDL Event and return true if the event has been used
+   */
+  void process_event(const SDL_Event& event);
+
+  void write(lisp::Writer& writer);
+  void read(const lisp::Lisp& lisp);
+  void reset();
+
+  Menu* get_key_options_menu();
+  Menu* get_joystick_options_menu();
+
+private:
+  void process_key_event(const SDL_Event& event);
+  void process_hat_event(const SDL_JoyHatEvent& jhat);
+  void process_axis_event(const SDL_JoyAxisEvent& jaxis);
+  void process_button_event(const SDL_JoyButtonEvent& jbutton);
+  void process_console_key_event(const SDL_Event& event);
+  void process_menu_key_event(const SDL_Event& event);
+
+  void print_joystick_mappings();
+
+  typedef std::map<SDLKey, Control> KeyMap;
+  KeyMap keymap;
+
+  typedef std::map<int, Control> ButtonMap;
+  ButtonMap joy_button_map;
+
+  typedef std::map<int, Control> AxisMap;
+  AxisMap joy_axis_map;
+
+  typedef std::map<int, Control> HatMap;
+  HatMap joy_hat_map;
+
+  std::vector<SDL_Joystick*> joysticks;
+
+  std::string name;
+
+  int dead_zone;
+  /// the number of buttons all joysticks have
+  int min_joybuttons;
+  /// the max number of buttons a joystick has
+  int max_joybuttons;
+
+  int max_joyaxis;
+
+  int max_joyhats;
+
+  Uint8 hat_state;
+
+  bool jump_with_up; 
+
+  SDLKey reversemap_key(Control c);
+  int    reversemap_joybutton(Control c);
+  int    reversemap_joyaxis(Control c);
+  int    reversemap_joyhat(Control c);
+
+  void unbind_joystick_control(Control c);
+
+  void bind_joybutton(int button, Control c);
+  void bind_joyaxis(int axis, Control c);
+  void bind_joyhat(int dir, Control c);
+  void bind_key(SDLKey key, Control c);
+
+  void set_joy_controls(Control id, bool value);
+
+  int wait_for_key;
+  int wait_for_joystick;
+
+  class KeyboardMenu;
+  class JoystickMenu;
+
+  KeyboardMenu* key_options_menu;
+  JoystickMenu* joystick_options_menu;
+  friend class KeyboardMenu;
+  friend class JoystickMenu;
+};
+
+#endif
diff --git a/src/direction.hpp b/src/direction.hpp
new file mode 100644 (file)
index 0000000..e665d20
--- /dev/null
@@ -0,0 +1,25 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef SUPERTUX_DIRECTION_H
+#define SUPERTUX_DIRECTION_H
+
+enum Direction { AUTO, LEFT, RIGHT, UP, DOWN };
+
+#endif
diff --git a/src/fadeout.cpp b/src/fadeout.cpp
new file mode 100644 (file)
index 0000000..283771e
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "fadeout.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+
+FadeOut::FadeOut(float fade_time, Color color)
+    : color(color), fade_time(fade_time), accum_time(0)
+{
+}
+
+FadeOut::~FadeOut()
+{
+}
+
+void
+FadeOut::update(float elapsed_time)
+{
+  accum_time += elapsed_time;
+  if(accum_time > fade_time)
+    accum_time = fade_time;
+}
+
+void
+FadeOut::draw(DrawingContext& context)
+{
+  Color col = color;
+  col.alpha = accum_time / fade_time;
+  context.draw_filled_rect(Vector(0, 0),
+                           Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+                           col, LAYER_GUI+1);
+}
+
+bool
+FadeOut::done()
+{
+  return accum_time >= fade_time;
+}
diff --git a/src/fadeout.hpp b/src/fadeout.hpp
new file mode 100644 (file)
index 0000000..b005b8c
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __FADEOUT_HPP__
+#define __FADEOUT_HPP__
+
+#include "video/color.hpp"
+#include "screen_fade.hpp"
+
+/**
+ * Fades a screen towards a specific color
+ */
+class FadeOut : public ScreenFade
+{
+public:
+  FadeOut(float fade_time, Color dest_color = Color(0, 0, 0));
+  virtual ~FadeOut();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+  /// returns true if the effect is completed
+  virtual bool done();
+
+private:
+  Color color;
+  float fade_time;
+  float accum_time;
+};
+
+#endif
diff --git a/src/file_system.cpp b/src/file_system.cpp
new file mode 100644 (file)
index 0000000..266da81
--- /dev/null
@@ -0,0 +1,110 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "log.hpp"
+#include "file_system.hpp"
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace FileSystem
+{
+
+std::string dirname(const std::string& filename)
+{
+  std::string::size_type p = filename.find_last_of('/');
+  if(p == std::string::npos)
+    return "";
+
+  return filename.substr(0, p+1);
+}
+
+std::string basename(const std::string& filename)
+{
+  std::string::size_type p = filename.find_last_of('/');
+  if(p == std::string::npos)
+    return filename;
+
+  return filename.substr(p+1, filename.size()-p-1);
+}
+
+std::string strip_extension(const std::string& filename)
+{
+  std::string::size_type p = filename.find_last_of('.');
+  if(p == std::string::npos)
+    return filename;
+
+  return filename.substr(0, p);
+}
+
+std::string normalize(const std::string& filename)
+{
+  std::vector<std::string> path_stack;
+
+  const char* p = filename.c_str();
+
+  while(true) {
+    while(*p == '/') {
+      p++;
+      continue;
+    }
+
+    const char* pstart = p;
+    while(*p != '/' && *p != 0) {
+      ++p;
+    }
+
+    size_t len = p - pstart;
+    if(len == 0)
+      break;
+
+    std::string pathelem(pstart, p-pstart);
+    if(pathelem == ".")
+      continue;
+
+    if(pathelem == "..") {
+      if(path_stack.empty()) {
+
+        log_warning << "Invalid '..' in path '" << filename << "'" << std::endl;
+        // push it into the result path so that the users sees his error...
+        path_stack.push_back(pathelem);
+      } else {
+        path_stack.pop_back();
+      }
+    } else {
+      path_stack.push_back(pathelem);
+    }
+  }
+
+  // construct path
+  std::ostringstream result;
+  for(std::vector<std::string>::iterator i = path_stack.begin();
+      i != path_stack.end(); ++i) {
+    result << '/' << *i;
+  }
+  if(path_stack.empty())
+    result << '/';
+
+  return result.str();
+}
+
+}
diff --git a/src/file_system.hpp b/src/file_system.hpp
new file mode 100644 (file)
index 0000000..b821188
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FILESYSTEM_H__
+#define __FILESYSTEM_H__
+
+#include <set>
+#include <string>
+
+namespace FileSystem
+{
+  std::string dirname(const std::string& filename);
+  std::string basename(const std::string& filename);
+
+  /**
+   * remove everything starting from and including the last dot
+   */
+  std::string strip_extension(const std::string& filename);
+
+  /**
+   * normalize filename so that "blup/bla/blo/../../bar" will become
+   * "blup/bar"
+   */
+  std::string normalize(const std::string& filename);
+}
+
+#endif
diff --git a/src/flip_level_transformer.cpp b/src/flip_level_transformer.cpp
new file mode 100644 (file)
index 0000000..83b8c4d
--- /dev/null
@@ -0,0 +1,136 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "flip_level_transformer.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "badguy/badguy.hpp"
+#include "sector.hpp"
+#include "tile_manager.hpp"
+#include "spawn_point.hpp"
+#include "object/platform.hpp"
+#include "object/block.hpp"
+
+void
+FlipLevelTransformer::transform_sector(Sector* sector)
+{
+  float height = sector->get_height();
+
+  for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
+      i != sector->gameobjects.end(); ++i) {
+    GameObject* object = *i;
+
+    TileMap* tilemap = dynamic_cast<TileMap*> (object);
+    if(tilemap) {
+      transform_tilemap(tilemap);
+    }
+    Player* player = dynamic_cast<Player*> (object);
+    if(player) {
+      Vector pos = player->get_pos();
+      pos.y = height - pos.y - player->get_bbox().get_height();
+      player->move(pos);
+      continue;
+    }
+    BadGuy* badguy = dynamic_cast<BadGuy*> (object);
+    if(badguy) {
+      transform_badguy(height, badguy);
+    }
+    Platform* platform = dynamic_cast<Platform*> (object);
+    if(platform) {
+      transform_platform(height, *platform);
+    }
+    Block* block = dynamic_cast<Block*> (object);
+    if(block) {
+      transform_block(height, *block);
+    }
+    MovingObject* mobject = dynamic_cast<MovingObject*> (object);
+    if(mobject) {
+      transform_moving_object(height, mobject);
+    }
+  }
+  for(Sector::SpawnPoints::iterator i = sector->spawnpoints.begin();
+      i != sector->spawnpoints.end(); ++i) {
+    transform_spawnpoint(height, *i);
+  }
+
+  if(sector->camera != 0 && sector->player != 0)
+    sector->camera->reset(sector->player->get_pos());
+}
+
+void
+FlipLevelTransformer::transform_tilemap(TileMap* tilemap)
+{
+  for(size_t x = 0; x < tilemap->get_width(); ++x) {
+    for(size_t y = 0; y < tilemap->get_height()/2; ++y) {
+      // swap tiles
+      int y2 = tilemap->get_height()-1-y;
+      const Tile* t1 = tilemap->get_tile(x, y);
+      const Tile* t2 = tilemap->get_tile(x, y2);
+      tilemap->change(x, y, t2->getID());
+      tilemap->change(x, y2, t1->getID());
+    }
+  }
+  if(tilemap->get_drawing_effect() != 0) {
+    tilemap->set_drawing_effect(NO_EFFECT);
+  } else {
+    tilemap->set_drawing_effect(VERTICAL_FLIP);
+  }
+}
+
+void
+FlipLevelTransformer::transform_badguy(float height, BadGuy* badguy)
+{
+  Vector pos = badguy->get_start_position();
+  pos.y = height - pos.y;
+  badguy->set_start_position(pos);
+}
+
+void
+FlipLevelTransformer::transform_spawnpoint(float height, SpawnPoint* spawn)
+{
+  Vector pos = spawn->pos;
+  pos.y = height - pos.y;
+  spawn->pos = pos;
+}
+
+void
+FlipLevelTransformer::transform_moving_object(float height, MovingObject*object)
+{
+  Vector pos = object->get_pos();
+  pos.y = height - pos.y - object->get_bbox().get_height();
+  object->set_pos(pos);
+}
+
+void
+FlipLevelTransformer::transform_platform(float height, Platform& platform)
+{
+  Path& path = platform.get_path();
+  for (std::vector<Path::Node>::iterator i = path.nodes.begin(); i != path.nodes.end(); i++) {
+    Vector& pos = i->position;
+    pos.y = height - pos.y - platform.get_bbox().get_height();
+  }
+}
+
+void
+FlipLevelTransformer::transform_block(float height, Block& block)
+{
+  block.original_y = height - block.original_y - block.get_bbox().get_height();
+}
diff --git a/src/flip_level_transformer.hpp b/src/flip_level_transformer.hpp
new file mode 100644 (file)
index 0000000..832fe08
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __FLIP_LEVEL_TRANSFORMER_H__
+#define __FLIP_LEVEL_TRANSFORMER_H__
+
+#include "level_transformer.hpp"
+
+class TileMap;
+class BadGuy;
+class SpawnPoint;
+class MovingObject;
+class Platform;
+class Block;
+
+/** Vertically or horizontally flip a level */
+class FlipLevelTransformer : public LevelTransformer
+{
+public:
+  virtual void transform_sector(Sector* sector);
+
+private:
+  void transform_tilemap(TileMap* tilemap);
+  void transform_moving_object(float height, MovingObject* object);
+  void transform_badguy(float height, BadGuy* badguy);
+  void transform_spawnpoint(float height, SpawnPoint* spawnpoint);
+  void transform_platform(float height, Platform& platform);
+  void transform_block(float height, Block& block);
+};
+
+#endif
diff --git a/src/game_object.cpp b/src/game_object.cpp
new file mode 100644 (file)
index 0000000..273367f
--- /dev/null
@@ -0,0 +1,72 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "log.hpp"
+#include "game_object.hpp"
+#include "object_remove_listener.hpp"
+
+GameObject::GameObject()
+  : wants_to_die(false), remove_listeners(NULL)
+{
+}
+
+GameObject::~GameObject()
+{
+  // call remove listeners (and remove them from the list)
+  RemoveListenerListEntry* entry = remove_listeners;
+  while(entry != NULL) {
+    RemoveListenerListEntry* next = entry->next;
+    entry->listener->object_removed(this);
+    delete entry;
+    entry = next;
+  }
+}
+
+void 
+GameObject::add_remove_listener(ObjectRemoveListener* listener)
+{
+  RemoveListenerListEntry* entry = new RemoveListenerListEntry();
+  entry->next = remove_listeners;
+  entry->listener = listener;
+  remove_listeners = entry;
+}
+
+void
+GameObject::del_remove_listener(ObjectRemoveListener* listener)
+{
+  RemoveListenerListEntry* entry = remove_listeners;
+  if (entry->listener == listener) {
+    remove_listeners = entry->next;
+    delete entry;
+    return;
+  }
+  RemoveListenerListEntry* next = entry->next;
+  while(next != NULL) {
+    if (next->listener == listener) {
+      entry->next = next->next;
+      delete next;
+      break;
+    }
+    entry = next;
+    next = next->next;
+  }
+}
+
diff --git a/src/game_object.hpp b/src/game_object.hpp
new file mode 100644 (file)
index 0000000..5acc833
--- /dev/null
@@ -0,0 +1,105 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_GAMEOBJECT_H
+#define SUPERTUX_GAMEOBJECT_H
+
+#include <string>
+#include "refcounter.hpp"
+
+class DrawingContext;
+class ObjectRemoveListener;
+
+/**
+ * This is a base class for all game objects. Each sector of a level will hold a
+ * list of active GameObject while the game is played.
+ *
+ * This class is responsible for:
+ *  - Updating and Drawing the object. This should happen in the update() and
+ *    draw() functions. Both are called once per frame.
+ *  - Providing a safe way to remove the object by calling the remove_me
+ *    functions.
+ */
+class GameObject : public RefCounter
+{
+public:
+  GameObject();
+  virtual ~GameObject();
+
+  /** This function is called once per frame and allows the object to update
+   * it's state. The elapsed_time is the time since the last frame in
+   * seconds and should be the base for all timed calculations (don't use
+   * SDL_GetTicks directly as this will fail in pause mode)
+   */
+  virtual void update(float elapsed_time) = 0;
+
+  /** The GameObject should draw itself onto the provided DrawingContext if this
+   * function is called.
+   */
+  virtual void draw(DrawingContext& context) = 0;
+
+  /** returns true if the object is not scheduled to be removed yet */
+  bool is_valid() const
+  {
+    return !wants_to_die;
+  }
+
+  /** schedules this object to be removed at the end of the frame */
+  void remove_me()
+  {
+    wants_to_die = true;
+  }
+
+  /** registers a remove listener which will be called if the object
+   * gets removed/destroyed
+   */
+  void add_remove_listener(ObjectRemoveListener* listener);
+  
+  /** 
+   * unregisters a remove listener, so it will no longer be called if the object
+   * gets removed/destroyed
+   */
+  void del_remove_listener(ObjectRemoveListener* listener);
+
+  const std::string& get_name() const
+  {
+    return name;
+  }
+
+private:
+  /** this flag indicates if the object should be removed at the end of the
+   * frame
+   */
+  bool wants_to_die;
+
+  struct RemoveListenerListEntry
+  {
+    RemoveListenerListEntry* next;
+    ObjectRemoveListener* listener;
+  };
+  RemoveListenerListEntry* remove_listeners;
+
+protected:
+  /**
+   * a name for the gameobject, this is mostly a hint for scripts and for
+   * debugging, don't rely on names being set or being unique
+   */
+  std::string name;
+};
+
+#endif /*SUPERTUX_GAMEOBJECT_H*/
diff --git a/src/game_session.cpp b/src/game_session.cpp
new file mode 100644 (file)
index 0000000..0a61cd3
--- /dev/null
@@ -0,0 +1,647 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdexcept>
+
+#include <SDL.h>
+
+#include "game_session.hpp"
+#include "log.hpp"
+#include "console.hpp"
+#include "worldmap/worldmap.hpp"
+#include "mainloop.hpp"
+#include "audio/sound_manager.hpp"
+#include "gui/menu.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "tile.hpp"
+#include "player_status.hpp"
+#include "object/particlesystem.hpp"
+#include "object/background.hpp"
+#include "object/gradient.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/player.hpp"
+#include "object/level_time.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "timer.hpp"
+#include "options_menu.hpp"
+#include "textscroller.hpp"
+#include "control/codecontroller.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "main.hpp"
+#include "file_system.hpp"
+#include "gameconfig.hpp"
+#include "gettext.hpp"
+#include "console.hpp"
+#include "flip_level_transformer.hpp"
+#include "trigger/secretarea_trigger.hpp"
+#include "trigger/sequence_trigger.hpp"
+#include "random_generator.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "object/endsequence_walkright.hpp"
+#include "object/endsequence_walkleft.hpp"
+#include "object/endsequence_fireworks.hpp"
+#include "direction.hpp"
+#include "scripting/time_scheduler.hpp"
+
+// the engine will be run with a logical framerate of 64fps.
+// We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
+// binary fraction...
+static const float LOGICAL_FPS = 64.0;
+
+enum GameMenuIDs {
+  MNID_CONTINUE,
+  MNID_ABORTLEVEL
+};
+
+GameSession* GameSession::current_ = NULL;
+
+GameSession::GameSession(const std::string& levelfile_, Statistics* statistics)
+  : level(0), currentsector(0),
+    end_sequence(0),
+    levelfile(levelfile_), best_level_statistics(statistics),
+    capture_demo_stream(0), playback_demo_stream(0), demo_controller(0),
+    play_time(0)
+{
+  current_ = this;
+  currentsector = NULL;
+
+  game_pause = false;
+  speed_before_pause = main_loop->get_speed();
+
+  statistics_backdrop.reset(new Surface("images/engine/menu/score-backdrop.png"));
+
+  restart_level();
+
+  game_menu.reset(new Menu());
+  game_menu->add_label(_("Pause"));
+  game_menu->add_hl();
+  game_menu->add_entry(MNID_CONTINUE, _("Continue"));
+  game_menu->add_submenu(_("Options"), get_options_menu());
+  game_menu->add_hl();
+  game_menu->add_entry(MNID_ABORTLEVEL, _("Abort Level"));
+}
+
+void
+GameSession::restart_level()
+{
+  game_pause   = false;
+  end_sequence = 0;
+
+  main_controller->reset();
+
+  currentsector = 0;
+
+  level.reset(new Level);
+  level->load(levelfile);
+  level->stats.total_coins = level->get_total_coins();
+  level->stats.total_badguys = level->get_total_badguys();
+  level->stats.total_secrets = level->get_total_count<SecretAreaTrigger>();
+  level->stats.reset();
+  if(reset_sector != "")level->stats.declare_invalid();
+
+  if(reset_sector != "") {
+    currentsector = level->get_sector(reset_sector);
+    if(!currentsector) {
+      std::stringstream msg;
+      msg << "Couldn't find sector '" << reset_sector << "' for resetting tux.";
+      throw std::runtime_error(msg.str());
+    }
+    currentsector->activate(reset_pos);
+  } else {
+    currentsector = level->get_sector("main");
+    if(!currentsector)
+      throw std::runtime_error("Couldn't find main sector");
+    currentsector->activate("main");
+  }
+
+  //levelintro();
+
+  sound_manager->stop_music();
+  currentsector->play_music(LEVEL_MUSIC);
+
+  if(capture_file != "") {
+    int newSeed=0;               // next run uses a new seed
+    while (newSeed == 0)            // which is the next non-zero random num.
+        newSeed = systemRandom.rand();
+    config->random_seed = systemRandom.srand(newSeed);
+    log_info << "Next run uses random seed " <<config->random_seed <<std::endl;
+    record_demo(capture_file);
+  }
+}
+
+GameSession::~GameSession()
+{
+  delete capture_demo_stream;
+  delete playback_demo_stream;
+  delete demo_controller;
+  free_options_menu();
+
+  current_ = NULL;
+}
+
+void
+GameSession::record_demo(const std::string& filename)
+{
+  delete capture_demo_stream;
+
+  capture_demo_stream = new std::ofstream(filename.c_str());
+  if(!capture_demo_stream->good()) {
+    std::stringstream msg;
+    msg << "Couldn't open demo file '" << filename << "' for writing.";
+    throw std::runtime_error(msg.str());
+  }
+  capture_file = filename;
+
+  char buf[30];                            // save the seed in the demo file
+  snprintf(buf, sizeof(buf), "random_seed=%10d", config->random_seed);
+  for (int i=0; i==0 || buf[i-1]; i++)
+    capture_demo_stream->put(buf[i]);
+}
+
+int
+GameSession::get_demo_random_seed(const std::string& filename)
+{
+  std::istream* test_stream = new std::ifstream(filename.c_str());
+  if(test_stream->good()) {
+    char buf[30];                     // recall the seed from the demo file
+    int seed;
+    for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
+      test_stream->get(buf[i]);
+    if (sscanf(buf, "random_seed=%10d", &seed) == 1) {
+      log_info << "Random seed " << seed << " from demo file" << std::endl;
+         return seed;
+    }
+    else
+      log_info << "Demo file contains no random number" << std::endl;
+  }
+  return 0;
+}
+
+void
+GameSession::play_demo(const std::string& filename)
+{
+  delete playback_demo_stream;
+  delete demo_controller;
+
+  playback_demo_stream = new std::ifstream(filename.c_str());
+  if(!playback_demo_stream->good()) {
+    std::stringstream msg;
+    msg << "Couldn't open demo file '" << filename << "' for reading.";
+    throw std::runtime_error(msg.str());
+  }
+
+  Player& tux = *currentsector->player;
+  demo_controller = new CodeController();
+  tux.set_controller(demo_controller);
+
+  // skip over random seed, if it exists in the file
+  char buf[30];                            // ascii decimal seed
+  int seed;
+  for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
+    playback_demo_stream->get(buf[i]);
+  if (sscanf(buf, "random_seed=%010d", &seed) != 1)
+    playback_demo_stream->seekg(0);     // old style w/o seed, restart at beg
+}
+
+void
+GameSession::levelintro()
+{
+  sound_manager->stop_music();
+
+  DrawingContext context;
+  for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
+      i != currentsector->gameobjects.end(); ++i) {
+    Background* background = dynamic_cast<Background*> (*i);
+    if(background) {
+      background->draw(context);
+    }
+    Gradient* gradient = dynamic_cast<Gradient*> (*i);
+    if(gradient) {
+      gradient->draw(context);
+    }
+  }
+
+//  context.draw_text(gold_text, level->get_name(), Vector(SCREEN_WIDTH/2, 160),
+//      ALIGN_CENTER, LAYER_FOREGROUND1);
+  context.draw_center_text(gold_text, level->get_name(), Vector(0, 160),
+      LAYER_FOREGROUND1);
+
+  std::stringstream ss_coins;
+  ss_coins << _("Coins") << ": " << player_status->coins;
+  context.draw_text(white_text, ss_coins.str(), Vector(SCREEN_WIDTH/2, 210),
+      ALIGN_CENTER, LAYER_FOREGROUND1);
+
+  if((level->get_author().size()) && (level->get_author() != "SuperTux Team"))
+    context.draw_text(white_small_text,
+      std::string(_("contributed by ")) + level->get_author(),
+      Vector(SCREEN_WIDTH/2, 350), ALIGN_CENTER, LAYER_FOREGROUND1);
+
+  if(best_level_statistics != NULL)
+    best_level_statistics->draw_message_info(context, _("Best Level Statistics"));
+
+  wait_for_event(1.0, 3.0);
+}
+
+void
+GameSession::on_escape_press()
+{
+  if(currentsector->player->is_dying() || end_sequence)
+  {
+    // Let the timers run out, we fast-forward them to force past a sequence
+    if (end_sequence)
+      end_sequence->stop();
+
+    currentsector->player->dying_timer.start(FLT_EPSILON);
+    return;   // don't let the player open the menu, when he is dying
+  }
+
+  if(level->on_menukey_script != "") {
+    std::istringstream in(level->on_menukey_script);
+    run_script(in, "OnMenuKeyScript");
+  } else {
+    toggle_pause();
+  }
+}
+
+void
+GameSession::toggle_pause()
+{
+  if(!game_pause) {
+    speed_before_pause = main_loop->get_speed();
+    main_loop->set_speed(0);
+    Menu::set_current(game_menu.get());
+    game_menu->set_active_item(MNID_CONTINUE);
+    game_pause = true;
+  } else {
+    main_loop->set_speed(speed_before_pause);
+    Menu::set_current(NULL);
+    game_pause = false;
+  }
+}
+
+HSQUIRRELVM
+GameSession::run_script(std::istream& in, const std::string& sourcename)
+{
+  using namespace Scripting;
+
+  // garbage collect thread list
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ) {
+    HSQOBJECT& object = *i;
+    HSQUIRRELVM vm = object_to_vm(object);
+
+    if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+      sq_release(global_vm, &object);
+      i = scripts.erase(i);
+      continue;
+    }
+
+    ++i;
+  }
+
+  HSQOBJECT object = create_thread(global_vm);
+  scripts.push_back(object);
+
+  HSQUIRRELVM vm = object_to_vm(object);
+
+  compile_and_run(vm, in, sourcename);
+
+  return vm;
+}
+
+void
+GameSession::process_events()
+{
+  // end of pause mode?
+  if(!Menu::current() && game_pause) {
+    game_pause = false;
+  }
+
+  // playback a demo?
+  if(playback_demo_stream != 0) {
+    demo_controller->update();
+    char left = false;
+    char right = false;
+    char up = false;
+    char down = false;
+    char jump = false;
+    char action = false;
+    playback_demo_stream->get(left);
+    playback_demo_stream->get(right);
+    playback_demo_stream->get(up);
+    playback_demo_stream->get(down);
+    playback_demo_stream->get(jump);
+    playback_demo_stream->get(action);
+    demo_controller->press(Controller::LEFT, left);
+    demo_controller->press(Controller::RIGHT, right);
+    demo_controller->press(Controller::UP, up);
+    demo_controller->press(Controller::DOWN, down);
+    demo_controller->press(Controller::JUMP, jump);
+    demo_controller->press(Controller::ACTION, action);
+  }
+
+  // save input for demo?
+  if(capture_demo_stream != 0) {
+    capture_demo_stream ->put(main_controller->hold(Controller::LEFT));
+    capture_demo_stream ->put(main_controller->hold(Controller::RIGHT));
+    capture_demo_stream ->put(main_controller->hold(Controller::UP));
+    capture_demo_stream ->put(main_controller->hold(Controller::DOWN));
+    capture_demo_stream ->put(main_controller->hold(Controller::JUMP));
+    capture_demo_stream ->put(main_controller->hold(Controller::ACTION));
+  }
+}
+
+void
+GameSession::check_end_conditions()
+{
+  Player* tux = currentsector->player;
+
+  /* End of level? */
+  if(end_sequence && end_sequence->is_done()) {
+    finish(true);
+  } else if (!end_sequence && tux->is_dead()) {
+    restart_level();
+  }
+}
+
+void
+GameSession::draw(DrawingContext& context)
+{
+  currentsector->draw(context);
+  drawstatus(context);
+
+  if(game_pause)
+    draw_pause(context);
+}
+
+void
+GameSession::draw_pause(DrawingContext& context)
+{
+  context.draw_filled_rect(
+      Vector(0,0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+      Color(.2f, .2f, .2f, .5f), LAYER_FOREGROUND1);
+}
+
+void
+GameSession::process_menu()
+{
+  Menu* menu = Menu::current();
+  if(menu) {
+    menu->update();
+
+    if(menu == game_menu.get()) {
+      switch (game_menu->check()) {
+        case MNID_CONTINUE:
+          Menu::set_current(0);
+          toggle_pause();
+          break;
+        case MNID_ABORTLEVEL:
+          Menu::set_current(0);
+          main_loop->exit_screen();
+          break;
+      }
+    }
+  }
+}
+
+void
+GameSession::setup()
+{
+  Menu::set_current(NULL);
+  current_ = this;
+
+  if(currentsector != Sector::current()) {
+       currentsector->activate(currentsector->player->get_pos());
+  }
+  currentsector->play_music(LEVEL_MUSIC);
+
+  // Eat unneeded events
+  SDL_Event event;
+  while(SDL_PollEvent(&event))
+  {}
+}
+
+void
+GameSession::update(float elapsed_time)
+{
+  // handle controller
+  if(main_controller->pressed(Controller::PAUSE_MENU))
+    on_escape_press();
+
+  process_events();
+  process_menu();
+
+  check_end_conditions();
+
+  // respawning in new sector?
+  if(newsector != "" && newspawnpoint != "") {
+    Sector* sector = level->get_sector(newsector);
+    if(sector == 0) {
+      log_warning << "Sector '" << newsector << "' not found" << std::endl;
+    }
+    sector->activate(newspawnpoint);
+    sector->play_music(LEVEL_MUSIC);
+    currentsector = sector;
+    newsector = "";
+    newspawnpoint = "";
+  }
+
+  // Update the world state and all objects in the world
+  if(!game_pause) {
+    // Update the world
+    if (!end_sequence) {
+      play_time += elapsed_time; //TODO: make sure we don't count cutscene time
+      level->stats.time = play_time;
+      currentsector->update(elapsed_time);
+    } else {
+      if (!end_sequence->is_tux_stopped()) {
+        currentsector->update(elapsed_time);
+      } else {
+        end_sequence->update(elapsed_time);
+      }
+    }
+  }
+
+  // update sounds
+  sound_manager->set_listener_position(currentsector->player->get_pos());
+
+  /* Handle music: */
+  if (end_sequence)
+    return;
+
+  if(currentsector->player->invincible_timer.started()) {
+    if(currentsector->player->invincible_timer.get_timeleft() <=
+       TUX_INVINCIBLE_TIME_WARNING) {
+      currentsector->play_music(HERRING_WARNING_MUSIC);
+    } else {
+      currentsector->play_music(HERRING_MUSIC);
+    }
+  } else if(currentsector->get_music_type() != LEVEL_MUSIC) {
+    currentsector->play_music(LEVEL_MUSIC);
+  }
+}
+
+void
+GameSession::finish(bool win)
+{
+  using namespace WorldMapNS;
+
+  if(win) {
+    if(WorldMap::current())
+      WorldMap::current()->finished_level(level.get());
+  }
+
+  main_loop->exit_screen();
+}
+
+void
+GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
+{
+  newsector = sector;
+  newspawnpoint = spawnpoint;
+}
+
+void
+GameSession::set_reset_point(const std::string& sector, const Vector& pos)
+{
+  reset_sector = sector;
+  reset_pos = pos;
+}
+
+std::string
+GameSession::get_working_directory()
+{
+  return FileSystem::dirname(levelfile);
+}
+
+void
+GameSession::display_info_box(const std::string& text)
+{
+  InfoBox* box = new InfoBox(text);
+
+  bool running = true;
+  DrawingContext context;
+
+  while(running)  {
+
+    // TODO make a screen out of this, another mainloop is ugly
+    main_controller->update();
+    SDL_Event event;
+    while (SDL_PollEvent(&event)) {
+      main_controller->process_event(event);
+      if(event.type == SDL_QUIT)
+        main_loop->quit();
+    }
+
+    if(main_controller->pressed(Controller::JUMP)
+        || main_controller->pressed(Controller::ACTION)
+        || main_controller->pressed(Controller::PAUSE_MENU)
+        || main_controller->pressed(Controller::MENU_SELECT))
+      running = false;
+    else if(main_controller->pressed(Controller::DOWN))
+      box->scrolldown();
+    else if(main_controller->pressed(Controller::UP))
+      box->scrollup();
+    box->draw(context);
+    draw(context);
+    context.do_drawing();
+    sound_manager->update();
+  }
+
+  delete box;
+}
+
+void
+GameSession::start_sequence(const std::string& sequencename)
+{
+  // handle special "stoptux" sequence
+  if (sequencename == "stoptux") {
+    if (!end_sequence) {
+      log_warning << "Final target reached without an active end sequence" << std::endl;
+      this->start_sequence("endsequence");
+    }
+    if (end_sequence) end_sequence->stop_tux();
+    return;
+  }
+
+  // abort if a sequence is already playing
+  if (end_sequence)
+         return;
+
+  if (sequencename == "endsequence") {
+    if (currentsector->get_players()[0]->physic.get_velocity_x() < 0) {
+      end_sequence = new EndSequenceWalkLeft();
+    } else {
+      end_sequence = new EndSequenceWalkRight();
+    }
+  } else if (sequencename == "fireworks") {
+    end_sequence = new EndSequenceFireworks();
+  } else {
+    log_warning << "Unknown sequence '" << sequencename << "'. Ignoring." << std::endl;
+    return;
+  }
+
+  /* slow down the game for end-sequence */
+  main_loop->set_speed(0.5f);
+
+  currentsector->add_object(end_sequence);
+  end_sequence->start();
+
+  sound_manager->play_music("music/leveldone.ogg", false);
+  currentsector->player->invincible_timer.start(10000.0f);
+
+  // Stop all clocks.
+  for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin();
+                 i != currentsector->gameobjects.end(); ++i)
+  {
+    GameObject* obj = *i;
+
+    LevelTime* lt = dynamic_cast<LevelTime*> (obj);
+    if(lt)
+      lt->stop();
+  }
+}
+
+/* (Status): */
+void
+GameSession::drawstatus(DrawingContext& context)
+{
+  player_status->draw(context);
+
+  // draw level stats while end_sequence is running
+  if (end_sequence) {
+    level->stats.draw_endseq_panel(context, best_level_statistics, statistics_backdrop.get());
+  }
+}
diff --git a/src/game_session.hpp b/src/game_session.hpp
new file mode 100644 (file)
index 0000000..fdb8f4f
--- /dev/null
@@ -0,0 +1,144 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_GAMELOOP_H
+#define SUPERTUX_GAMELOOP_H
+
+#include <string>
+#include <SDL.h>
+#include <squirrel.h>
+#include "screen.hpp"
+#include "math/vector.hpp"
+#include "video/surface.hpp"
+#include "object/endsequence.hpp"
+
+class Level;
+class Sector;
+class Statistics;
+class DrawingContext;
+class CodeController;
+class Menu;
+
+/**
+ * The GameSession class controlls the controll flow of the Game (the part
+ * where you actually play a level)
+ */
+class GameSession : public Screen
+{
+public:
+  GameSession(const std::string& levelfile, Statistics* statistics = NULL);
+  ~GameSession();
+
+  void record_demo(const std::string& filename);
+  int get_demo_random_seed(const std::string& filename);
+  void play_demo(const std::string& filename);
+
+  void draw(DrawingContext& context);
+  void update(float frame_ratio);
+  void setup();
+
+  void set_current()
+  { current_ = this; }
+  static GameSession* current()
+  { return current_; }
+
+  /// ends the current level
+  void finish(bool win = true);
+  void respawn(const std::string& sectorname, const std::string& spawnpointname);
+  void set_reset_point(const std::string& sectorname, const Vector& pos);
+  std::string get_reset_point_sectorname()
+  { return reset_sector; }
+
+  Vector get_reset_point_pos()
+  { return reset_pos; }
+
+  void display_info_box(const std::string& text);
+
+  Sector* get_current_sector()
+  { return currentsector; }
+
+  Level* get_current_level()
+  { return level.get(); }
+
+  void start_sequence(const std::string& sequencename);
+
+  /**
+   * returns the "working directory" usually this is the directory where the
+   * currently played level resides. This is used when locating additional
+   * resources for the current level/world
+   */
+  std::string get_working_directory();
+  void restart_level();
+
+  void toggle_pause();
+
+private:
+  void check_end_conditions();
+  void process_events();
+  void capture_demo_step();
+
+  void levelintro();
+  void drawstatus(DrawingContext& context);
+  void draw_pause(DrawingContext& context);
+
+  HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+  void on_escape_press();
+  void process_menu();
+
+  std::auto_ptr<Level> level;
+  std::auto_ptr<Surface> statistics_backdrop;
+
+  // scripts
+  typedef std::vector<HSQOBJECT> ScriptList;
+  ScriptList scripts;
+
+  Sector* currentsector;
+
+  int levelnb;
+  int pause_menu_frame;
+
+  EndSequence* end_sequence;
+
+  bool  game_pause;
+  float speed_before_pause;
+
+  std::string levelfile;
+
+  // reset point (the point where tux respawns if he dies)
+  std::string reset_sector;
+  Vector reset_pos;
+
+  // the sector and spawnpoint we should spawn after this frame
+  std::string newsector;
+  std::string newspawnpoint;
+
+  static GameSession* current_;
+
+  Statistics* best_level_statistics;
+
+  std::ostream* capture_demo_stream;
+  std::string capture_file;
+  std::istream* playback_demo_stream;
+  CodeController* demo_controller;
+
+  std::auto_ptr<Menu> game_menu;
+
+  float play_time; /**< total time in seconds that this session ran interactively */
+};
+
+#endif /*SUPERTUX_GAMELOOP_H*/
diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp
new file mode 100644 (file)
index 0000000..060eed0
--- /dev/null
@@ -0,0 +1,130 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "gameconfig.hpp"
+
+#include <stdlib.h>
+#include <string>
+#include <stdexcept>
+
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+Config* config = 0;
+
+Config::Config()
+{
+  use_fullscreen = true;
+  video = AUTO_VIDEO;
+  try_vsync = true;
+  show_fps = false;
+  sound_enabled = true;
+  music_enabled = true;
+  console_enabled = false;
+  random_seed = 0;          // set by time(), by default (unless in config)
+
+  screenwidth = 800;
+  screenheight = 600;
+  aspect_ratio = -1;       // autodetect
+
+  enable_script_debugger = false;
+
+  locale = ""; // autodetect 
+}
+
+Config::~Config()
+{}
+
+void
+Config::load()
+{
+  lisp::Parser parser;
+  const lisp::Lisp* root = parser.parse("config");
+
+  const lisp::Lisp* config_lisp = root->get_lisp("supertux-config");
+  if(!config_lisp)
+    throw std::runtime_error("File is not a supertux-config file");
+
+  config_lisp->get("show_fps", show_fps);
+  config_lisp->get("console", console_enabled);
+  config_lisp->get("locale", locale);
+  config_lisp->get("random_seed", random_seed);
+
+  const lisp::Lisp* config_video_lisp = config_lisp->get_lisp("video");
+  if(config_video_lisp) {
+    config_video_lisp->get("fullscreen", use_fullscreen);
+    std::string video_string;
+    config_video_lisp->get("video", video_string);
+    video = get_video_system(video_string);
+    config_video_lisp->get("vsync", try_vsync);
+    config_video_lisp->get("width", screenwidth);
+    config_video_lisp->get("height", screenheight);
+    config_video_lisp->get("aspect_ratio", aspect_ratio);
+  }
+
+  const lisp::Lisp* config_audio_lisp = config_lisp->get_lisp("audio");
+  if(config_audio_lisp) {
+    config_audio_lisp->get("sound_enabled", sound_enabled);
+    config_audio_lisp->get("music_enabled", music_enabled);
+  }
+
+  const lisp::Lisp* config_control_lisp = config_lisp->get_lisp("control");
+  if(config_control_lisp && main_controller) {
+    main_controller->read(*config_control_lisp);
+  }
+}
+
+void
+Config::save()
+{
+  lisp::Writer writer("config");
+
+  writer.start_list("supertux-config");
+
+  writer.write_bool("show_fps", show_fps);
+  writer.write_bool("console", console_enabled);
+  writer.write_string("locale", locale);
+
+  writer.start_list("video");
+  writer.write_bool("fullscreen", use_fullscreen);
+  writer.write_string("video", get_video_string(video));
+  writer.write_bool("vsync", try_vsync);
+  writer.write_int("width", screenwidth);
+  writer.write_int("height", screenheight);
+  writer.write_float("aspect_ratio", aspect_ratio);
+  writer.end_list("video");
+
+  writer.start_list("audio");
+  writer.write_bool("sound_enabled", sound_enabled);
+  writer.write_bool("music_enabled", music_enabled);
+  writer.end_list("audio");
+
+  if(main_controller) {
+    writer.start_list("control");
+    main_controller->write(writer);
+    writer.end_list("control");
+  }
+
+  writer.end_list("supertux-config");
+}
diff --git a/src/gameconfig.hpp b/src/gameconfig.hpp
new file mode 100644 (file)
index 0000000..ac45730
--- /dev/null
@@ -0,0 +1,66 @@
+//  $Id$
+//
+//  SuperTux=
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_CONFIG_H
+#define SUPERTUX_CONFIG_H
+
+#include <config.h>
+
+#include <string>
+
+#include "video/video_systems.hpp"
+
+class Config
+{
+public:
+  Config();
+  ~Config();
+
+  void load();
+  void save();
+
+  /** screen width in pixel (warning: this is the real screen width+height,
+   * you should use the logical SCREEN_WIDTH and SCREEN_HEIGHT for your
+   * rendering code.)
+   */
+  int screenwidth;
+  int screenheight;
+  float aspect_ratio;
+
+  bool use_fullscreen;
+  VideoSystem video;
+  bool try_vsync;
+  bool show_fps;
+  bool sound_enabled;
+  bool music_enabled;
+  bool console_enabled;
+
+  int random_seed;            // initial random seed.  0 ==> set from time()
+
+  /** this variable is set if supertux should start in a specific level */
+  std::string start_level;
+  bool enable_script_debugger;
+  std::string start_demo;
+  std::string record_demo;
+
+  std::string locale; /**< force SuperTux language to this locale, e.g. "de". A file "data/locale/xx.po" must exist for this to work. An empty string means autodetect. */
+};
+
+extern Config* config;
+
+#endif
diff --git a/src/gettext.hpp b/src/gettext.hpp
new file mode 100644 (file)
index 0000000..a2bd003
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H
+
+#include "tinygettext/tinygettext.hpp"
+
+extern TinyGetText::DictionaryManager dictionary_manager;
+
+static inline const char* _(const char* message)
+{
+  return dictionary_manager.get_dictionary().translate(message);
+}
+
+static inline std::string _(const std::string& message)
+{
+  return dictionary_manager.get_dictionary().translate(message);
+}
+
+static inline const char* N_(const char* id, const char* id2, int num)
+{
+  return dictionary_manager.get_dictionary().translate(id, id2, num).c_str();
+}
+
+#endif /* _LIBGETTEXT_H */
diff --git a/src/gui/button.cpp b/src/gui/button.cpp
new file mode 100644 (file)
index 0000000..6b16a13
--- /dev/null
@@ -0,0 +1,257 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <SDL.h>
+#include <iostream>
+
+#include "main.hpp"
+#include "button.hpp"
+#include "mousecursor.hpp"
+#include "video/font.hpp"
+#include "video/surface.hpp"
+
+Font* Button::info_font = 0;
+extern SDL_Surface* screen;
+
+/* Buttons */
+
+Button::Button(Surface* image_, std::string info_, SDLKey binding_)
+  : binding(binding_)
+{
+  image = image_;
+  size = Vector(image->get_width(), image->get_height());
+  id = 0;
+  info = info_;
+}
+
+Button::~Button()
+{
+}
+
+void Button::draw(DrawingContext &context, bool selected)
+{
+if(selected)
+  context.draw_filled_rect(pos, size, Color (200,240,220), LAYER_GUI);
+else
+  context.draw_filled_rect(pos, size, Color (200,200,220), LAYER_GUI);
+
+Vector tanslation = -context.get_translation();
+if(state == BT_SHOW_INFO)
+  {
+  Vector offset;
+  if(pos.x + tanslation.x < 100 && pos.y + tanslation.y > SCREEN_HEIGHT - 20)
+    offset = Vector(size.x, - 10);
+  else if(pos.x + tanslation.x < 100)
+    offset = Vector(size.x, 0);
+  else
+    offset = Vector(-30, -size.y/2);
+  context.draw_text(info_font, info, pos + offset, ALIGN_LEFT, LAYER_GUI+2);
+  if(binding != 0)
+    context.draw_text(info_font, "(" + std::string(SDL_GetKeyName(binding)) +
+                                 ")", pos + offset + Vector(0,12),
+                                 ALIGN_LEFT,  LAYER_GUI+2);
+  }
+
+context.draw_surface_part(image, Vector(0,0), size, pos, LAYER_GUI+1);
+}
+
+int Button::event(SDL_Event &event, int x_offset, int y_offset)
+{
+state = BT_NONE;
+switch(event.type)
+  {
+  case SDL_MOUSEBUTTONDOWN:
+    if(event.button.x > pos.x + x_offset && event.button.x < pos.x + x_offset + size.x &&
+       event.button.y > pos.y + y_offset && event.button.y < pos.y + y_offset + size.y)
+      {
+      if(event.button.button == SDL_BUTTON_RIGHT)
+        state = BT_SHOW_INFO;
+      }
+    break;
+  case SDL_MOUSEBUTTONUP:
+    if(event.button.x > pos.x + x_offset && event.button.x < pos.x + x_offset + size.x &&
+       event.button.y > pos.y + y_offset && event.button.y < pos.y + y_offset + size.y)
+      {
+      if(event.button.button == SDL_BUTTON_LEFT)
+        state = BT_SELECTED;
+      }
+    break;
+  case SDL_KEYDOWN:    // key pressed
+    if(event.key.keysym.sym == binding)
+      state = BT_SELECTED;
+    break;
+  default:
+    break;
+  }
+return state;
+}
+
+/* Group of buttons */
+
+ButtonGroup::ButtonGroup(Vector pos_, Vector buttons_size_, Vector buttons_box_)
+  : pos(pos_), buttons_size(buttons_size_), buttons_box(buttons_box_)
+{
+buttons.clear();
+row = 0;
+button_selected = -1;
+mouse_hover = false;
+mouse_left_button = false;
+buttons_pair_nb = 0;
+}
+
+ButtonGroup::~ButtonGroup()
+{
+}
+
+void ButtonGroup::add_button(Button button, int id, bool select)
+{
+button.pos.x = ((buttons.size()-buttons_pair_nb) % (int)buttons_box.x) * buttons_size.x;
+button.pos.y = ((int)((buttons.size()-buttons_pair_nb) / buttons_box.x)) * buttons_size.y;
+button.size = buttons_size;
+button.id = id;
+if(select)
+  button_selected = id;
+
+buttons.push_back(button);
+}
+
+void ButtonGroup::add_pair_of_buttons(Button button1, int id1, Button button2, int id2)
+{
+button1.pos.x = button2.pos.x = ((buttons.size()-buttons_pair_nb) % (int)buttons_box.x) * buttons_size.x;
+button1.pos.y = button2.pos.y = ((int)((buttons.size()-buttons_pair_nb) / buttons_box.x)) * buttons_size.y;
+button1.size.x = button2.size.x = buttons_size.x;
+button1.size.y = button2.size.y = buttons_size.y / 2;
+button2.pos.y += buttons_size.y / 2;
+button1.id = id1;
+button2.id = id2;
+
+buttons_pair_nb++;
+buttons.push_back(button1);
+buttons.push_back(button2);
+}
+
+void ButtonGroup::draw(DrawingContext &context)
+{
+context.draw_filled_rect(pos - Vector(12,4),
+        Vector(buttons_size.x*buttons_box.x + 16, buttons_size.y*buttons_box.y + 8),
+        Color (0,0,0, 128), LAYER_GUI-1);
+
+context.push_transform();
+context.set_translation(Vector(-pos.x, -pos.y + buttons_size.y*row));
+for(Buttons::iterator i = buttons.begin(); i != buttons.end(); ++i)
+  {
+  if(i->pos.y < row*buttons_size.y ||
+      i->pos.y + i->size.y > (row + buttons_box.y) * buttons_size.y)
+    continue;
+
+  i->draw(context, i->id == button_selected);
+  }
+context.pop_transform();
+}
+
+bool ButtonGroup::event(SDL_Event &event)
+{
+bool caught_event = false;
+
+switch(event.type)
+  {
+  case SDL_MOUSEMOTION:
+    mouse_hover = false;
+
+    if(mouse_left_button)
+      {
+      pos.x += int(event.motion.xrel * float(SCREEN_WIDTH)/screen->w);
+      pos.y += int(event.motion.yrel * float(SCREEN_HEIGHT)/screen->h);
+      caught_event = true;
+      }
+    if(event.button.x > pos.x-12 && event.button.x < pos.x+16 + buttons_box.x*buttons_size.x &&
+       event.button.y > pos.y-4 && event.button.y < pos.y+8 + buttons_box.y*buttons_size.y)
+      mouse_hover = true;
+    break;
+  case SDL_MOUSEBUTTONDOWN:
+    if(event.button.x < pos.x-12 || event.button.x > pos.x+16 +
+        buttons_box.x*buttons_size.x || event.button.y < pos.y-4 ||
+        event.button.y > pos.y+8 + buttons_box.y*buttons_size.y)
+      break;
+
+    caught_event = true;
+
+    if(event.button.button == SDL_BUTTON_WHEELUP)
+      {
+      row--;
+      if(row < 0)
+        row = 0;
+      }
+    else if(event.button.button == SDL_BUTTON_WHEELDOWN)
+      {
+      row++;
+      if(row > (int)((buttons.size()-buttons_pair_nb)/buttons_box.x) - (int)buttons_box.y +
+               ((int)(buttons.size()-buttons_pair_nb)%(int)buttons_box.x != 0 ? 1 : 0))
+        row = (int)((buttons.size()-buttons_pair_nb)/buttons_box.x) - (int)buttons_box.y +
+              ((int)(buttons.size()-buttons_pair_nb)%(int)buttons_box.x != 0 ? 1 : 0);
+      }
+    else if(event.button.button == SDL_BUTTON_LEFT)
+      mouse_left_button = true;
+    else
+      caught_event = false;
+    break;
+  case SDL_MOUSEBUTTONUP:
+    mouse_left_button = false;
+    break;
+  default:
+    break;
+  }
+
+if(caught_event)
+  return true;
+
+for(Buttons::iterator i = buttons.begin(); i != buttons.end(); ++i)
+  {
+  if(i->pos.y < row*buttons_size.y ||
+      i->pos.y + i->size.y > (row + buttons_box.y) * buttons_size.y)
+    continue;
+
+  if(i->event(event, (int)pos.x,
+                     (int)pos.y - row*(int)buttons_size.y) == BT_SELECTED)
+    {
+    button_selected = i->id;
+    caught_event = true;
+    break;
+    }
+  }
+
+return caught_event;
+}
+
+int ButtonGroup::selected_id()
+{
+return button_selected;
+}
+
+void ButtonGroup::set_unselected()
+{
+button_selected = -1;
+}
+
+bool ButtonGroup::is_hover()
+{
+return mouse_hover;
+}
diff --git a/src/gui/button.hpp b/src/gui/button.hpp
new file mode 100644 (file)
index 0000000..b81f51d
--- /dev/null
@@ -0,0 +1,91 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_BUTTON_H
+#define SUPERTUX_BUTTON_H
+
+#include <vector>
+#include <string>
+
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+
+class Surface;
+
+class ButtonGroup;
+
+enum {
+  BT_NONE,
+  BT_HOVER,
+  BT_SELECTED,
+  BT_SHOW_INFO
+  };
+
+class Button
+{
+public:
+  Button(Surface* image_, std::string info_, SDLKey binding_);
+  ~Button();
+
+  void draw(DrawingContext& context, bool selected);
+  int event(SDL_Event& event, int x_offset = 0, int y_offset = 0);
+
+  static Font* info_font;
+
+private:
+  friend class ButtonGroup;
+
+  Vector pos, size;
+
+  Surface* image;
+  SDLKey binding;
+
+  int id;
+  int state;
+  std::string info;
+};
+
+class ButtonGroup
+{
+public:
+  ButtonGroup(Vector pos_, Vector size_, Vector button_box_);
+  ~ButtonGroup();
+
+  void draw(DrawingContext& context);
+  bool event(SDL_Event& event);
+
+  void add_button(Button button, int id, bool select = false);
+  void add_pair_of_buttons(Button button1, int id1, Button button2, int id2);
+
+  int selected_id();
+  void set_unselected();
+  bool is_hover();
+
+private:
+  Vector pos, buttons_size, buttons_box;
+  typedef std::vector <Button> Buttons;
+  Buttons buttons;
+
+  int button_selected, row;
+  bool mouse_hover, mouse_left_button;
+
+  int buttons_pair_nb;
+};
+
+#endif
diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp
new file mode 100644 (file)
index 0000000..8983515
--- /dev/null
@@ -0,0 +1,829 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+#include <cassert>
+#include <stdexcept>
+
+#include "menu.hpp"
+#include "mainloop.hpp"
+#include "video/drawing_context.hpp"
+#include "gettext.hpp"
+#include "math/vector.hpp"
+#include "main.hpp"
+#include "resources.hpp"
+#include "timer.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+
+static const float MENU_REPEAT_INITIAL = 0.4f;
+static const float MENU_REPEAT_RATE = 0.2f;
+static const float FLICK_CURSOR_TIME = 0.5f;
+
+extern SDL_Surface* screen;
+
+std::vector<Menu*> Menu::last_menus;
+Menu* Menu::current_ = 0;
+Font* Menu::default_font;
+Font* Menu::active_font;
+Font* Menu::deactive_font;
+Font* Menu::label_font;
+Font* Menu::field_font;
+
+/* just displays a Yes/No text that can be used to confirm stuff */
+bool confirm_dialog(Surface *background, std::string text)
+{
+  //Surface* cap_screen = Surface::CaptureScreen();
+  Menu* dialog = new Menu;
+  dialog->add_deactive(-1, text);
+  dialog->add_hl();
+  dialog->add_entry(true, _("Yes"));
+  dialog->add_entry(false, _("No"));
+  dialog->add_hl();
+
+  Menu::set_current(dialog);
+
+  DrawingContext context;
+
+  // TODO make this a screen and not another mainloop...
+  while(true)
+    {
+      SDL_Event event;
+      while (SDL_PollEvent(&event)) {
+        if(event.type == SDL_QUIT)
+          main_loop->quit();
+        main_controller->process_event(event);
+        dialog->event(event);
+      }
+
+      if(background == NULL)
+        context.draw_gradient(Color(0.8f, 0.95f, 0.85f), Color(0.8f, 0.8f, 0.8f),
+                              LAYER_BACKGROUND0);
+      else
+        context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
+
+      dialog->draw(context);
+      dialog->update();
+
+      switch (dialog->check())
+        {
+        case true:
+          //delete cap_screen;
+          Menu::set_current(0);
+          delete dialog;
+          return true;
+          break;
+        case false:
+          //delete cap_screen;
+          Menu::set_current(0);
+          delete dialog;
+          return false;
+          break;
+        default:
+          break;
+        }
+
+      mouse_cursor->draw(context);
+      context.do_drawing();
+      SDL_Delay(25);
+    }
+
+  return false;
+}
+
+void
+Menu::push_current(Menu* pmenu)
+{
+  if (current_)
+    last_menus.push_back(current_);
+
+  current_ = pmenu;
+  current_->effect_time = real_time;
+}
+
+void
+Menu::pop_current()
+{
+  if (last_menus.size() >= 1) {
+    current_ = last_menus.back();
+    current_->effect_time = real_time;
+    last_menus.pop_back();
+  } else {
+    current_ = 0;
+  }
+}
+
+void
+Menu::set_current(Menu* menu)
+{
+  last_menus.clear();
+
+  if (menu)
+    menu->effect_time = real_time;
+
+  current_ = menu;
+  // just to be sure...
+  main_controller->reset();
+}
+
+MenuItem::MenuItem(MenuItemKind _kind, int _id)
+  : kind(_kind) , id(_id)
+{
+  toggled = false;
+  selected = false;
+  target_menu = 0;
+}
+
+void
+MenuItem::change_text(const  std::string& text_)
+{
+  text = text_;
+}
+
+void
+MenuItem::change_input(const  std::string& text_)
+{
+  input = text_;
+}
+
+std::string MenuItem::get_input_with_symbol(bool active_item)
+{
+  if(!active_item) {
+    input_flickering = true;
+  } else {
+    input_flickering = ((int) (real_time / FLICK_CURSOR_TIME)) % 2;
+  }
+
+  char str[1024];
+  if(input_flickering)
+    snprintf(str, sizeof(str), "%s ",input.c_str());
+  else
+    snprintf(str, sizeof(str), "%s_",input.c_str());
+
+  std::string string = str;
+
+  return string;
+}
+
+Menu::~Menu()
+{
+  for(std::vector<MenuItem*>::iterator i = items.begin();
+      i != items.end(); ++i)
+    delete *i;
+  if(current_ == this)
+    current_ = NULL;
+}
+
+Menu::Menu()
+{
+  hit_item = -1;
+  menuaction = MENU_ACTION_NONE;
+  delete_character = 0;
+  mn_input_char = '\0';
+
+  pos_x        = SCREEN_WIDTH/2;
+  pos_y        = SCREEN_HEIGHT/2;
+  arrange_left = 0;
+  active_item  = -1;
+
+  checkbox.reset(new Surface("images/engine/menu/checkbox-unchecked.png"));
+  checkbox_checked.reset(new Surface("images/engine/menu/checkbox-checked.png"));
+  back.reset(new Surface("images/engine/menu/arrow-back.png"));
+  arrow_left.reset(new Surface("images/engine/menu/arrow-left.png"));
+  arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
+}
+
+void Menu::set_pos(float x, float y, float rw, float rh)
+{
+  pos_x = x + get_width() * rw;
+  pos_y = y + get_height() * rh;
+}
+
+/* Add an item to a menu */
+void
+Menu::additem(MenuItem* item)
+{
+  items.push_back(item);
+
+  /* If a new menu is being built, the active item shouldn't be set to
+   * something that isnt selectable. Set the active_item to the first
+   * selectable item added
+   */
+  if (active_item == -1
+      && item->kind != MN_HL
+      && item->kind != MN_LABEL
+      && item->kind != MN_DEACTIVE) {
+    active_item = items.size() - 1;
+  }
+}
+
+void
+Menu::add_hl()
+{
+  additem(new MenuItem(MN_HL));
+}
+
+void
+Menu::add_label(const std::string& text)
+{
+  MenuItem* item = new MenuItem(MN_LABEL);
+  item->text = text;
+  additem(item);
+}
+
+void
+Menu::add_controlfield(int id, const std::string& text,
+               const std::string& mapping)
+{
+  MenuItem* item = new MenuItem(MN_CONTROLFIELD, id);
+  item->change_text(text);
+       item->change_input(mapping);
+  additem(item);
+}
+
+void
+Menu::add_entry(int id, const std::string& text)
+{
+  MenuItem* item = new MenuItem(MN_ACTION, id);
+  item->text = text;
+  additem(item);
+}
+
+void
+Menu::add_deactive(int id, const std::string& text)
+{
+  MenuItem* item = new MenuItem(MN_DEACTIVE, id);
+  item->text = text;
+  additem(item);
+}
+
+void
+Menu::add_toggle(int id, const std::string& text, bool toogled)
+{
+  MenuItem* item = new MenuItem(MN_TOGGLE, id);
+  item->text = text;
+  item->toggled = toogled;
+  additem(item);
+}
+
+void
+Menu::add_back(const std::string& text)
+{
+  MenuItem* item = new MenuItem(MN_BACK);
+  item->text = text;
+  additem(item);
+}
+
+void
+Menu::add_submenu(const std::string& text, Menu* submenu, int id)
+{
+  MenuItem* item = new MenuItem(MN_GOTO, id);
+  item->text = text;
+  item->target_menu = submenu;
+  additem(item);
+}
+
+void
+Menu::clear()
+{
+  for(std::vector<MenuItem*>::iterator i = items.begin();
+      i != items.end(); ++i) {
+    delete *i;
+  }
+  items.clear();
+  active_item = -1;
+}
+
+/* Process actions done on the menu */
+void
+Menu::update()
+{
+  /** check main input controller... */
+  if(main_controller->pressed(Controller::UP)) {
+    menuaction = MENU_ACTION_UP;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+  }
+  if(main_controller->hold(Controller::UP) &&
+      menu_repeat_time != 0 && real_time > menu_repeat_time) {
+    menuaction = MENU_ACTION_UP;
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
+  }
+  if(main_controller->pressed(Controller::DOWN)) {
+    menuaction = MENU_ACTION_DOWN;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+  }
+  if(main_controller->hold(Controller::DOWN) &&
+      menu_repeat_time != 0 && real_time > menu_repeat_time) {
+    menuaction = MENU_ACTION_DOWN;
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
+  }
+  if(main_controller->pressed(Controller::ACTION)
+     || main_controller->pressed(Controller::MENU_SELECT)) {
+    menuaction = MENU_ACTION_HIT;
+  }
+  if(main_controller->pressed(Controller::PAUSE_MENU)) {
+    menuaction = MENU_ACTION_BACK;
+  }
+
+  hit_item = -1;
+  if(items.size() == 0)
+    return;
+
+  int last_active_item = active_item;
+  switch(menuaction) {
+    case MENU_ACTION_UP:
+      do {
+        if (active_item > 0)
+          --active_item;
+        else
+          active_item = int(items.size())-1;
+      } while ((items[active_item]->kind == MN_HL
+                || items[active_item]->kind == MN_LABEL
+                || items[active_item]->kind == MN_DEACTIVE)
+               && (active_item != last_active_item));
+
+      break;
+
+    case MENU_ACTION_DOWN:
+      do {
+        if(active_item < int(items.size())-1 )
+          ++active_item;
+        else
+          active_item = 0;
+      } while ((items[active_item]->kind == MN_HL
+                || items[active_item]->kind == MN_LABEL
+                || items[active_item]->kind == MN_DEACTIVE)
+               && (active_item != last_active_item));
+
+      break;
+
+    case MENU_ACTION_LEFT:
+      if(items[active_item]->kind == MN_STRINGSELECT) {
+        if(items[active_item]->selected > 0)
+          items[active_item]->selected--;
+        else
+          items[active_item]->selected = items[active_item]->list.size()-1;
+      }
+      break;
+
+    case MENU_ACTION_RIGHT:
+      if(items[active_item]->kind == MN_STRINGSELECT) {
+        if(items[active_item]->selected+1 < items[active_item]->list.size())
+          items[active_item]->selected++;
+        else
+          items[active_item]->selected = 0;
+      }
+      break;
+
+    case MENU_ACTION_HIT: {
+      hit_item = active_item;
+      switch (items[active_item]->kind) {
+        case MN_GOTO:
+          assert(items[active_item]->target_menu != 0);
+          Menu::push_current(items[active_item]->target_menu);
+          break;
+
+        case MN_TOGGLE:
+          items[active_item]->toggled = !items[active_item]->toggled;
+          menu_action(items[active_item]);
+          break;
+
+        case MN_CONTROLFIELD:
+          menu_action(items[active_item]);
+          break;
+
+        case MN_ACTION:
+          menu_action(items[active_item]);
+          break;
+
+        case MN_TEXTFIELD:
+        case MN_NUMFIELD:
+          menuaction = MENU_ACTION_DOWN;
+          update();
+          break;
+
+        case MN_BACK:
+          Menu::pop_current();
+          break;
+        default:
+          break;
+      }
+      break;
+    }
+
+    case MENU_ACTION_REMOVE:
+      if(items[active_item]->kind == MN_TEXTFIELD
+         || items[active_item]->kind == MN_NUMFIELD)
+      {
+        if(!items[active_item]->input.empty())
+        {
+          int i = items[active_item]->input.size();
+
+          while(delete_character > 0)  /* remove charactes */
+          {
+            items[active_item]->input.resize(i-1);
+            delete_character--;
+          }
+        }
+      }
+      break;
+
+    case MENU_ACTION_INPUT:
+      if(items[active_item]->kind == MN_TEXTFIELD
+         || (items[active_item]->kind == MN_NUMFIELD
+             && mn_input_char >= '0' && mn_input_char <= '9'))
+      {
+        items[active_item]->input.push_back(mn_input_char);
+      }
+      break;
+
+    case MENU_ACTION_BACK:
+      Menu::pop_current();
+      break;
+
+    case MENU_ACTION_NONE:
+      break;
+  }
+  menuaction = MENU_ACTION_NONE;
+
+  assert(active_item < int(items.size()));
+}
+
+int
+Menu::check()
+{
+  if (hit_item != -1)
+    return items[hit_item]->id;
+  else
+    return -1;
+}
+
+void
+Menu::menu_action(MenuItem* )
+{}
+
+void
+Menu::draw_item(DrawingContext& context, int index)
+{
+  float menu_height = get_height();
+  float menu_width = get_width();
+
+  MenuItem& pitem = *(items[index]);
+
+  int effect_offset = 0;
+  if(effect_time != 0) {
+    if(real_time - effect_time > 0.5) {
+      effect_time = 0;
+    } else {
+      float effect_delta = (0.5 - (real_time - effect_time)) * 250;
+      effect_offset = (int) ((index % 2) ? effect_delta : -effect_delta);
+    }
+  }
+
+  Font* text_font = default_font;
+  float x_pos       = pos_x;
+  float y_pos       = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
+  int shadow_size = 2;
+  int text_width  = int(text_font->get_text_width(pitem.text));
+  int input_width = int(text_font->get_text_width(pitem.input) + 10);
+  int list_width = 0;
+  if(pitem.list.size() > 0) {
+    list_width = (int) text_font->get_text_width(pitem.list[pitem.selected]);
+  }
+
+  if (arrange_left)
+    x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
+
+  if(index == active_item)
+    {
+      shadow_size = 3;
+      text_font = active_font;
+    }
+
+  switch (pitem.kind)
+    {
+    case MN_DEACTIVE:
+      {
+        context.draw_text(deactive_font, pitem.text,
+                          Vector(SCREEN_WIDTH/2, y_pos - int(deactive_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
+        break;
+      }
+
+    case MN_HL:
+      {
+        // TODO
+        float x = pos_x - menu_width/2;
+        float y = y_pos - 12 - effect_offset;
+        /* Draw a horizontal line with a little 3d effect */
+        context.draw_filled_rect(Vector(x, y + 6),
+                                 Vector(menu_width, 4),
+                                 Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
+        context.draw_filled_rect(Vector(x, y + 6),
+                                 Vector(menu_width, 2),
+                                 Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
+        break;
+      }
+    case MN_LABEL:
+      {
+        context.draw_text(label_font, pitem.text,
+                          Vector(SCREEN_WIDTH/2, y_pos - int(label_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
+        break;
+      }
+    case MN_TEXTFIELD:
+    case MN_NUMFIELD:
+    case MN_CONTROLFIELD:
+      {
+        float width = text_width + input_width + 5;
+        float text_pos = SCREEN_WIDTH/2 - width/2;
+        float input_pos = text_pos + text_width + 10;
+
+        context.draw_filled_rect(
+          Vector(input_pos - 5, y_pos - 10),
+          Vector(input_width + 10, 20),
+          Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI-5);
+        context.draw_filled_rect(
+          Vector(input_pos - 4, y_pos - 9),
+          Vector(input_width + 8, 18),
+          Color(0, 0, 0, 0.5f), LAYER_GUI-4);
+
+        if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
+          {
+            if(active_item == index)
+              context.draw_text(field_font,
+                                pitem.get_input_with_symbol(true),
+                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+                                ALIGN_LEFT, LAYER_GUI);
+            else
+              context.draw_text(field_font,
+                                pitem.get_input_with_symbol(false),
+                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+                                ALIGN_LEFT, LAYER_GUI);
+          }
+        else
+          context.draw_text(field_font, pitem.input,
+                            Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+                            ALIGN_LEFT, LAYER_GUI);
+
+        context.draw_text(text_font, pitem.text,
+                          Vector(text_pos, y_pos - int(text_font->get_height()/2)),
+                          ALIGN_LEFT, LAYER_GUI);
+        break;
+      }
+    case MN_STRINGSELECT:
+      {
+        int list_pos_2 = list_width + 16;
+        int list_pos   = list_width/2;
+        int text_pos   = (text_width + 16)/2;
+
+        /* Draw arrows */
+        context.draw_surface(arrow_left.get(),
+                             Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
+                             LAYER_GUI);
+        context.draw_surface(arrow_right.get(),
+                             Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8),
+                             LAYER_GUI);
+
+        /* Draw input background */
+        context.draw_filled_rect(
+          Vector(x_pos - list_pos + text_pos - 1, y_pos - 10),
+          Vector(list_pos_2 + 2, 20),
+          Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI - 4);
+        context.draw_filled_rect(
+          Vector(x_pos - list_pos + text_pos, y_pos - 9),
+          Vector(list_pos_2, 18),
+          Color(0, 0, 0, 0.5f), LAYER_GUI - 5);
+
+        context.draw_text(text_font, pitem.list[pitem.selected],
+                                 Vector(SCREEN_WIDTH/2 + text_pos, y_pos - int(text_font->get_height()/2)),
+                                 ALIGN_CENTER, LAYER_GUI);
+        context.draw_text(text_font, pitem.text,
+                                 Vector(SCREEN_WIDTH/2  + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
+                                 ALIGN_CENTER, LAYER_GUI);
+        break;
+      }
+    case MN_BACK:
+      {
+        context.draw_text(text_font, pitem.text,
+                          Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
+        context.draw_surface(back.get(),
+                             Vector(x_pos + text_width/2  + 16, y_pos - 8),
+                             LAYER_GUI);
+        break;
+      }
+
+    case MN_TOGGLE:
+      {
+        context.draw_text(text_font, pitem.text,
+                          Vector(SCREEN_WIDTH/2, y_pos - (text_font->get_height()/2)),
+                          ALIGN_CENTER, LAYER_GUI);
+
+        if(pitem.toggled)
+          context.draw_surface(checkbox_checked.get(),
+                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
+                               LAYER_GUI + 1);
+        else
+          context.draw_surface(checkbox.get(),
+                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
+                               LAYER_GUI + 1);
+        break;
+      }
+    case MN_ACTION:
+      context.draw_text(text_font, pitem.text,
+                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI);
+      break;
+
+    case MN_GOTO:
+      context.draw_text(text_font, pitem.text,
+                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI);
+      break;
+    }
+}
+
+float Menu::get_width() const
+{
+  /* The width of the menu has to be more than the width of the text
+     with the most characters */
+  float menu_width = 0;
+  for(unsigned int i = 0; i < items.size(); ++i)
+  {
+    Font* font = default_font;
+    if(items[i]->kind == MN_LABEL)
+      font = label_font;
+
+    float w = font->get_text_width(items[i]->text) +
+        label_font->get_text_width(items[i]->input) + 16;
+    if(items[i]->kind == MN_TOGGLE)
+      w += 32;
+
+    if(w > menu_width)
+      menu_width = w;
+  }
+
+  return menu_width + 24;
+}
+
+float Menu::get_height() const
+{
+  return items.size() * 24;
+}
+
+/* Draw the current menu. */
+void
+Menu::draw(DrawingContext& context)
+{
+  if(MouseCursor::current()) {
+    MouseCursor::current()->draw(context);
+  }
+
+  float menu_height = get_height();
+  float menu_width = get_width();
+
+  /* Draw a transparent background */
+  context.draw_filled_rect(
+    Vector(pos_x - menu_width/2, pos_y - 24*items.size()/2 - 10),
+    Vector(menu_width,menu_height + 20),
+    Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-10);
+
+  for(unsigned int i = 0; i < items.size(); ++i)
+    {
+      draw_item(context, i);
+    }
+}
+
+MenuItem&
+Menu::get_item_by_id(int id)
+{
+  for(std::vector<MenuItem*>::iterator i = items.begin();
+      i != items.end(); ++i) {
+    MenuItem& item = **i;
+
+    if(item.id == id)
+      return item;
+  }
+
+  throw std::runtime_error("MenuItem not found");
+}
+
+const MenuItem&
+Menu::get_item_by_id(int id) const
+{
+  for(std::vector<MenuItem*>::const_iterator i = items.begin();
+      i != items.end(); ++i) {
+    const MenuItem& item = **i;
+
+    if(item.id == id)
+      return item;
+  }
+
+  throw std::runtime_error("MenuItem not found");
+}
+
+int Menu::get_active_item_id()
+{
+  return items[active_item]->id;
+}
+
+bool
+Menu::is_toggled(int id) const
+{
+  return get_item_by_id(id).toggled;
+}
+
+/* Check for menu event */
+void
+Menu::event(const SDL_Event& event)
+{
+  if(effect_time != 0)
+    return;
+
+  switch(event.type) {
+    case SDL_MOUSEBUTTONDOWN:
+      {
+        int x = int(event.motion.x * float(SCREEN_WIDTH)/screen->w);
+        int y = int(event.motion.y * float(SCREEN_HEIGHT)/screen->h);
+
+        if(x > pos_x - get_width()/2 &&
+            x < pos_x + get_width()/2 &&
+            y > pos_y - get_height()/2 &&
+            y < pos_y + get_height()/2)
+          {
+            menuaction = MENU_ACTION_HIT;
+          }
+      }
+      break;
+
+    case SDL_MOUSEMOTION:
+      {
+        float x = event.motion.x * SCREEN_WIDTH/screen->w;
+        float y = event.motion.y * SCREEN_HEIGHT/screen->h;
+
+        if(x > pos_x - get_width()/2 &&
+            x < pos_x + get_width()/2 &&
+            y > pos_y - get_height()/2 &&
+            y < pos_y + get_height()/2)
+          {
+            int new_active_item
+              = static_cast<int> ((y - (pos_y - get_height()/2)) / 24);
+
+            /* only change the mouse focus to a selectable item */
+            if ((items[new_active_item]->kind != MN_HL)
+                && (items[new_active_item]->kind != MN_LABEL)
+                && (items[new_active_item]->kind != MN_DEACTIVE))
+              active_item = new_active_item;
+
+            if(MouseCursor::current())
+              MouseCursor::current()->set_state(MC_LINK);
+          }
+        else
+        {
+          if(MouseCursor::current())
+            MouseCursor::current()->set_state(MC_NORMAL);
+        }
+      }
+      break;
+
+    default:
+      break;
+    }
+}
+
+void
+Menu::set_active_item(int id)
+{
+  for(size_t i = 0; i < items.size(); ++i) {
+    MenuItem* item = items[i];
+    if(item->id == id) {
+      active_item = i;
+      break;
+    }
+  }
+}
diff --git a/src/gui/menu.hpp b/src/gui/menu.hpp
new file mode 100644 (file)
index 0000000..5e85fa1
--- /dev/null
@@ -0,0 +1,207 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MENU_H
+#define SUPERTUX_MENU_H
+
+#include <vector>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <assert.h>
+
+#include <SDL.h>
+
+#include "video/surface.hpp"
+#include "video/font.hpp"
+#include "mousecursor.hpp"
+
+bool confirm_dialog(Surface* background, std::string text);
+
+/* Kinds of menu items */
+enum MenuItemKind {
+  MN_ACTION,
+  MN_GOTO,
+  MN_TOGGLE,
+  MN_BACK,
+  MN_DEACTIVE,
+  MN_TEXTFIELD,
+  MN_NUMFIELD,
+  MN_CONTROLFIELD,
+  MN_STRINGSELECT,
+  MN_LABEL,
+  MN_HL, /* horizontal line */
+};
+
+class Menu;
+
+class MenuItem
+{
+public:
+  MenuItem(MenuItemKind kind, int id = -1);
+  MenuItemKind kind;
+  int id;   // item id
+  bool toggled;
+  std::string text;
+  std::string input;
+
+  std::vector<std::string> list; // list of values for a STRINGSELECT item
+  size_t selected; // currently selected item
+
+  Menu* target_menu;
+
+  void change_text (const std::string& text);
+  void change_input(const std::string& text);
+
+  static MenuItem* create(MenuItemKind kind, const std::string& text,
+                          int init_toggle, Menu* target_menu, int id, int key);
+
+  std::string get_input_with_symbol(bool active_item);   // returns the text with an input symbol
+
+private:
+  /// copy-construction not allowed
+  MenuItem(const MenuItem& ) { assert(false); }
+  /// assignment not allowed
+  void operator= (const MenuItem& ) { assert(false); }
+
+  /// keyboard key or joystick button
+  bool input_flickering;
+};
+
+class Menu
+{
+private:
+  static std::vector<Menu*> last_menus;
+  static Menu* current_;
+
+  static void pop_current();
+
+public:
+  /** Set the current menu, if pmenu is NULL, hide the current menu */
+  static void set_current(Menu* pmenu);
+
+  static void push_current(Menu* pmenu);
+
+  /** Return the current active menu or NULL if none is active */
+  static Menu* current()
+  {
+    return current_;
+  }
+
+private:
+  /* Action done on the menu */
+  enum MenuAction {
+    MENU_ACTION_NONE = -1,
+    MENU_ACTION_UP,
+    MENU_ACTION_DOWN,
+    MENU_ACTION_LEFT,
+    MENU_ACTION_RIGHT,
+    MENU_ACTION_HIT,
+    MENU_ACTION_INPUT,
+    MENU_ACTION_REMOVE,
+    MENU_ACTION_BACK
+  };
+
+  /** Number of the item that got 'hit' (ie. pressed) in the last
+      event()/update() call, -1 if none */
+  int hit_item;
+
+  // position of the menu (ie. center of the menu, not top/left)
+  float pos_x;
+  float pos_y;
+
+  /** input event for the menu (up, down, left, right, etc.) */
+  MenuAction menuaction;
+
+  /* input implementation variables */
+  int delete_character;
+  char mn_input_char;
+  float menu_repeat_time;
+
+public:
+  static Font* default_font;
+  static Font* active_font;
+  static Font* deactive_font;
+  static Font* label_font;
+  static Font* field_font;
+
+  std::vector<MenuItem*> items;
+
+  Menu();
+  virtual ~Menu();
+
+  void add_hl();
+  void add_label(const std::string& text);
+  void add_entry(int id, const std::string& text);
+  void add_toggle(int id, const std::string& text, bool toggled = false);
+  void add_deactive(int id, const std::string& text);
+  void add_back(const std::string& text);
+  void add_submenu(const std::string& text, Menu* submenu, int id = -1);
+  void add_controlfield(int id, const std::string& text,
+                       const std::string& mapping = "");
+
+  virtual void menu_action(MenuItem* item);
+
+  void update();
+
+  /** Remove all entries from the menu */
+  void clear();
+
+  /** Return the index of the menu item that was 'hit' (ie. the user
+      clicked on it) in the last event() call */
+  int check ();
+
+  MenuItem& get_item(int index)
+  {
+    return *(items[index]);
+  }
+  MenuItem& get_item_by_id(int id);
+  const MenuItem& get_item_by_id(int id) const;
+
+  int get_active_item_id();
+  void set_active_item(int id);
+
+  void draw(DrawingContext& context);
+  void set_pos(float x, float y, float rw = 0, float rh = 0);
+
+  void event(const SDL_Event& event);
+
+  bool is_toggled(int id) const;
+
+protected:
+  void additem(MenuItem* pmenu_item);
+  float get_width() const;
+  float get_height() const;
+
+private:
+  void check_controlfield_change_event(const SDL_Event& event);
+  void draw_item(DrawingContext& context, int index);
+  float effect_time;
+  int arrange_left;
+  int active_item;
+
+  std::auto_ptr<Surface> checkbox;
+  std::auto_ptr<Surface> checkbox_checked;
+  std::auto_ptr<Surface> back;
+  std::auto_ptr<Surface> arrow_left;
+  std::auto_ptr<Surface> arrow_right;
+};
+
+#endif
diff --git a/src/gui/mousecursor.cpp b/src/gui/mousecursor.cpp
new file mode 100644 (file)
index 0000000..ba2da77
--- /dev/null
@@ -0,0 +1,85 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <SDL_events.h>
+#include <SDL_mouse.h>
+
+#include "video/drawing_context.hpp"
+#include "gui/mousecursor.hpp"
+#include "main.hpp"
+
+MouseCursor* MouseCursor::current_ = 0;
+extern SDL_Surface* screen;
+
+MouseCursor::MouseCursor(std::string cursor_file) : mid_x(0), mid_y(0)
+{
+  cursor = new Surface(cursor_file);
+
+  cur_state = MC_NORMAL;
+}
+
+MouseCursor::~MouseCursor()
+{
+  delete cursor;
+}
+
+int MouseCursor::state()
+{
+  return cur_state;
+}
+
+void MouseCursor::set_state(int nstate)
+{
+  cur_state = nstate;
+}
+
+void MouseCursor::set_mid(int x, int y)
+{
+  mid_x = x;
+  mid_y = y;
+}
+
+void MouseCursor::draw(DrawingContext& context)
+{
+  if(cur_state == MC_HIDE)
+    return;
+
+  int x,y,w,h;
+  Uint8 ispressed = SDL_GetMouseState(&x,&y);
+
+  x = int(x * float(SCREEN_WIDTH)/screen->w);
+  y = int(y * float(SCREEN_HEIGHT)/screen->h);
+
+  w = (int) cursor->get_width();
+  h = (int) (cursor->get_height() / MC_STATES_NB);
+  if(ispressed &SDL_BUTTON(1) || ispressed &SDL_BUTTON(2)) {
+    if(cur_state != MC_CLICK) {
+      state_before_click = cur_state;
+      cur_state = MC_CLICK;
+    }
+  } else {
+    if(cur_state == MC_CLICK)
+      cur_state = state_before_click;
+  }
+
+  context.draw_surface_part(cursor, Vector(0, h*cur_state),
+          Vector(w, h), Vector(x-mid_x, y-mid_y), LAYER_GUI+100);
+}
diff --git a/src/gui/mousecursor.hpp b/src/gui/mousecursor.hpp
new file mode 100644 (file)
index 0000000..b3cb53b
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MOUSECURSOR_H
+#define SUPERTUX_MOUSECURSOR_H
+
+#include <string>
+
+#include "video/surface.hpp"
+
+#define MC_STATES_NB 3
+
+enum {
+  MC_NORMAL = 0,
+  MC_CLICK,
+  MC_LINK,
+  MC_HIDE
+};
+
+class DrawingContext;
+
+/// Mouse cursor.
+/** Used to create mouse cursors.
+    The mouse cursors can be animated
+    and can be used in four different states.
+    (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+class MouseCursor
+{
+public:
+  /// Constructor of MouseCursor.
+  /** Expects an imagefile for the cursor and  the number of animation frames it contains. */
+  MouseCursor(std::string cursor_file);
+  ~MouseCursor();
+  /// Get MouseCursor state.
+  /** (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+  int state();
+  /// Set MouseCursor state.
+  /** (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+  void set_state(int nstate);
+  /// Define the middle of a MouseCursor.
+  /** Useful for cross mouse cursor images in example. */
+  void set_mid(int x, int y);
+
+  /// Draw MouseCursor on screen.
+  void draw(DrawingContext& context);
+
+  /// Return the current cursor.
+  static MouseCursor* current()
+  {        return current_;      };
+  /// Set current cursor.
+  static void set_current(MouseCursor* pcursor)
+  {        current_ = pcursor;      };
+
+private:
+  int mid_x, mid_y;
+  static MouseCursor* current_;
+  int state_before_click;
+  int cur_state;
+  Surface* cursor;
+};
+
+#endif /*SUPERTUX_MOUSECURSOR_H*/
diff --git a/src/level.cpp b/src/level.cpp
new file mode 100644 (file)
index 0000000..4b222bb
--- /dev/null
@@ -0,0 +1,236 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <map>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <memory>
+#include <stdexcept>
+
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/writer.hpp"
+#include "level.hpp"
+#include "physic.hpp"
+#include "sector.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "file_system.hpp"
+#include "object/gameobjs.hpp"
+#include "object/camera.hpp"
+#include "object/tilemap.hpp"
+#include "object/coin.hpp"
+#include "object/block.hpp"
+
+using namespace std;
+
+Level::Level()
+  : name("noname"), author("Mr. X")
+{
+}
+
+void
+Level::load(const std::string& filepath)
+{
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(filepath);
+
+    const lisp::Lisp* level = root->get_lisp("supertux-level");
+    if(!level)
+      throw std::runtime_error("file is not a supertux-level file.");
+
+    int version = 1;
+    level->get("version", version);
+    if(version == 1) {
+      load_old_format(*level);
+      return;
+    }
+
+    contact = "";
+    license = "";
+
+    lisp::ListIterator iter(level);
+    while(iter.next()) {
+      const std::string& token = iter.item();
+      if(token == "version") {
+        iter.value()->get(version);
+        if(version > 2) {
+          log_warning << "level format newer than application" << std::endl;
+        }
+      } else if(token == "name") {
+        iter.value()->get(name);
+      } else if(token == "author") {
+        iter.value()->get(author);
+      } else if(token == "contact") {
+        iter.value()->get(contact);
+      } else if(token == "license") {
+        iter.value()->get(license);
+      } else if(token == "on-menukey-script") {
+        iter.value()->get(on_menukey_script);
+      } else if(token == "sector") {
+        Sector* sector = new Sector(this);
+        sector->parse(*(iter.lisp()));
+        add_sector(sector);
+      } else {
+        log_warning << "Unknown token '" << token << "' in level file" << std::endl;
+      }
+    }
+
+  if (license == "") log_warning << "The level author did not specify a license for this level. You might not be allowed to share it." << std::endl;
+
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when reading level '" << filepath << "': " << e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
+
+void
+Level::load_old_format(const lisp::Lisp& reader)
+{
+  reader.get("name", name);
+  reader.get("author", author);
+
+  Sector* sector = new Sector(this);
+  sector->parse_old_format(reader);
+  add_sector(sector);
+}
+
+void
+Level::save(const std::string& filename)
+{
+  lisp::Writer* writer = new lisp::Writer(filename);
+
+  writer->write_comment("Level made using SuperTux's built-in Level Editor");
+
+  writer->start_list("supertux-level");
+
+  int version = 2;
+  writer->write_int("version", version);
+
+  writer->write_string("name", name, true);
+  writer->write_string("author", author);
+  if(on_menukey_script != "")
+    writer->write_string("on-menukey-script", on_menukey_script);
+
+  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+    Sector* sector = *i;
+    writer->start_list("sector");
+    sector->write(*writer);
+    writer->end_list("sector");
+  }
+
+  writer->end_list("supertux-level");
+
+  delete writer;
+}
+
+Level::~Level()
+{
+  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
+    delete *i;
+}
+
+void
+Level::add_sector(Sector* sector)
+{
+  Sector* test = get_sector(sector->get_name());
+  if(test != 0) {
+    throw std::runtime_error("Trying to add 2 sectors with same name");
+  }
+  sectors.push_back(sector);
+}
+
+Sector*
+Level::get_sector(const std::string& name)
+{
+  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+    Sector* sector = *i;
+    if(sector->get_name() == name)
+      return sector;
+  }
+
+  return 0;
+}
+
+size_t
+Level::get_sector_count()
+{
+  return sectors.size();
+}
+
+Sector*
+Level::get_sector(size_t num)
+{
+  return sectors.at(num);
+}
+
+int
+Level::get_total_coins()
+{
+  int total_coins = 0;
+  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+    Sector* sector = *i;
+    for(Sector::GameObjects::iterator o = sector->gameobjects.begin();
+        o != sector->gameobjects.end(); ++o) {
+      Coin* coin = dynamic_cast<Coin*> (*o);
+      if(coin)
+      {
+        total_coins++;
+        continue;
+      }
+      BonusBlock *block = dynamic_cast<BonusBlock*> (*o);
+      if(block)
+      {
+        if (block->contents == BonusBlock::CONTENT_COIN)
+        {
+          total_coins++;
+          continue;
+        }
+#if 0
+        // FIXME: do we want this? q.v. src/object/oneup.cpp
+        else if (block->contents == BonusBlock::CONTENT_1UP)
+        {
+          total_coins += 100;
+          continue;
+        }
+#endif
+      }
+    }
+  }
+  return total_coins;
+}
+
+int
+Level::get_total_badguys()
+{
+  int total_badguys = 0;
+  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
+    total_badguys += (*i)->get_total_badguys();
+  return total_badguys;
+}
diff --git a/src/level.hpp b/src/level.hpp
new file mode 100644 (file)
index 0000000..da2fdf4
--- /dev/null
@@ -0,0 +1,83 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_LEVEL_H
+#define SUPERTUX_LEVEL_H
+
+#include <vector>
+#include <string>
+#include "statistics.hpp"
+#include "sector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Level
+{
+public:
+  std::string name;
+  std::string author;
+  std::string contact;
+  std::string license;
+  std::string on_menukey_script;
+  typedef std::vector<Sector*> Sectors;
+  Sectors sectors;
+  Statistics stats;
+
+public:
+  Level();
+  ~Level();
+
+  // loads a levelfile
+  void load(const std::string& filename);
+  void save(const std::string& filename);
+
+  const std::string& get_name() const
+  { return name; }
+
+  const std::string& get_author() const
+  { return author; }
+
+  void add_sector(Sector* sector);
+
+  Sector* get_sector(const std::string& name);
+
+  size_t get_sector_count();
+  Sector* get_sector(size_t num);
+
+  int get_total_coins();
+  int get_total_badguys();
+
+  /** Get total number of GameObjects of given type */
+  template<class T> int get_total_count()
+  {
+    int total = 0;
+    for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+      total += (*i)->get_total_count<T>();
+    }
+    return total;
+  }
+
+private:
+  void load_old_format(const lisp::Lisp& reader);
+};
+
+#endif /*SUPERTUX_LEVEL_H*/
diff --git a/src/level_transformer.cpp b/src/level_transformer.cpp
new file mode 100644 (file)
index 0000000..a9366f5
--- /dev/null
@@ -0,0 +1,36 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <config.h>
+
+#include "level_transformer.hpp"
+#include "level.hpp"
+
+LevelTransformer::~LevelTransformer()
+{
+}
+
+void
+LevelTransformer::transform(Level* level)
+{
+  for(size_t i = 0; i < level->get_sector_count(); ++i) {
+    transform_sector(level->get_sector(i));
+  }
+}
diff --git a/src/level_transformer.hpp b/src/level_transformer.hpp
new file mode 100644 (file)
index 0000000..53f06b7
--- /dev/null
@@ -0,0 +1,44 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __LEVEL_TRANSFORMER_H__
+#define __LEVEL_TRANSFORMER_H__
+
+class Level;
+class Sector;
+
+/**
+ * This class is an abstract interface for algorithms that transform levels in
+ * some way before they are played.
+ */
+class LevelTransformer
+{
+public:
+  virtual ~LevelTransformer();
+
+  /** transform a complete Level, the standard implementation just calls
+   * transformSector on each sector in the level.
+   */
+  virtual void transform(Level* level);
+
+  virtual void transform_sector(Sector* sector) = 0;
+};
+
+#endif
diff --git a/src/lisp/lexer.cpp b/src/lisp/lexer.cpp
new file mode 100644 (file)
index 0000000..c9daf4b
--- /dev/null
@@ -0,0 +1,218 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <iostream>
+
+#include "lexer.hpp"
+
+namespace lisp
+{
+
+class EOFException
+{
+};
+
+Lexer::Lexer(std::istream& newstream)
+    : stream(newstream), eof(false), linenumber(0)
+{
+  try {
+    // trigger a refill of the buffer
+    c = 0;
+    bufend = 0;
+    nextChar();
+  } catch(EOFException& ) {
+  }
+}
+
+Lexer::~Lexer()
+{
+}
+
+void
+Lexer::nextChar()
+{
+  ++c;
+  if(c >= bufend) {
+    if(eof)
+      throw EOFException();
+    stream.read(buffer, BUFFER_SIZE);
+    size_t bytes_read = stream.gcount();
+
+    c = buffer;
+    bufend = buffer + bytes_read;
+
+    // the following is a hack that appends an additional ' ' at the end of
+    // the file to avoid problems when parsing symbols/elements and a sudden
+    // EOF. This is faster than relying on unget and IMO also nicer.
+    if(bytes_read == 0 || stream.eof()) {
+      eof = true;
+      *bufend = ' ';
+      ++bufend;
+    }
+  }
+}
+
+Lexer::TokenType
+Lexer::getNextToken()
+{
+  static const char* delims = "\"();";
+
+  try {
+    while(isspace(*c)) {
+      if(*c == '\n')
+        ++linenumber;
+      nextChar();
+    };
+
+    token_length = 0;
+
+    switch(*c) {
+      case ';': // comment
+        while(true) {
+          nextChar();
+          if(*c == '\n') {
+            ++linenumber;
+            break;
+          }
+        }
+        return getNextToken(); // and again
+      case '(':
+        nextChar();
+        return TOKEN_OPEN_PAREN;
+      case ')':
+        nextChar();
+        return TOKEN_CLOSE_PAREN;
+      case '"': {  // string
+        int startline = linenumber;
+        try {
+          while(1) {
+            nextChar();
+            if(*c == '"')
+              break;
+            else if (*c == '\r') // XXX this breaks with pure \r EOL
+              continue;
+            else if(*c == '\n')
+              linenumber++;
+            else if(*c == '\\') {
+              nextChar();
+              switch(*c) {
+                case 'n':
+                  *c = '\n';
+                  break;
+                case 't':
+                  *c = '\t';
+                  break;
+              }
+            }
+            if(token_length < MAX_TOKEN_LENGTH)
+              token_string[token_length++] = *c;
+          }
+          token_string[token_length] = 0;
+        } catch(EOFException& ) {
+          std::stringstream msg;
+          msg << "Parse error in line " << startline << ": "
+              << "EOF while parsing string.";
+          throw std::runtime_error(msg.str());
+        }
+        nextChar();
+        return TOKEN_STRING;
+      }
+      case '#': // constant
+        try {
+          nextChar();
+
+          while(isalnum(*c) || *c == '_') {
+            if(token_length < MAX_TOKEN_LENGTH)
+              token_string[token_length++] = *c;
+            nextChar();
+          }
+          token_string[token_length] = 0;
+        } catch(EOFException& ) {
+          std::stringstream msg;
+          msg << "Parse Error in line " << linenumber << ": "
+            << "EOF while parsing constant.";
+          throw std::runtime_error(msg.str());
+        }
+
+        if(strcmp(token_string, "t") == 0)
+          return TOKEN_TRUE;
+        if(strcmp(token_string, "f") == 0)
+          return TOKEN_FALSE;
+
+        // we only handle #t and #f constants at the moment...
+
+        {
+          std::stringstream msg;
+          msg << "Parse Error in line " << linenumber << ": "
+            << "Unknown constant '" << token_string << "'.";
+          throw std::runtime_error(msg.str());
+        }
+
+      default:
+        if(isdigit(*c) || *c == '-') {
+          bool have_nondigits = false;
+          bool have_digits = false;
+          int have_floating_point = 0;
+
+          do {
+            if(isdigit(*c))
+              have_digits = true;
+            else if(*c == '.')
+              ++have_floating_point;
+            else if(isalnum(*c) || *c == '_')
+              have_nondigits = true;
+
+            if(token_length < MAX_TOKEN_LENGTH)
+              token_string[token_length++] = *c;
+
+            nextChar();
+          } while(!isspace(*c) && !strchr(delims, *c));
+
+          token_string[token_length] = 0;
+
+          // no nextChar
+
+          if(have_nondigits || !have_digits || have_floating_point > 1)
+            return TOKEN_SYMBOL;
+          else if(have_floating_point == 1)
+            return TOKEN_REAL;
+          else
+            return TOKEN_INTEGER;
+        } else {
+          do {
+            if(token_length < MAX_TOKEN_LENGTH)
+              token_string[token_length++] = *c;
+            nextChar();
+          } while(!isspace(*c) && !strchr(delims, *c));
+          token_string[token_length] = 0;
+
+          // no nextChar
+
+          return TOKEN_SYMBOL;
+        }
+    }
+  } catch(EOFException& ) {
+    return TOKEN_EOF;
+  }
+}
+
+} // end of namespace lisp
diff --git a/src/lisp/lexer.hpp b/src/lisp/lexer.hpp
new file mode 100644 (file)
index 0000000..1cd062b
--- /dev/null
@@ -0,0 +1,69 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __LISPLEXER_H__
+#define __LISPLEXER_H__
+
+namespace lisp
+{
+
+class Lexer
+{
+public:
+  enum TokenType {
+    TOKEN_EOF,
+    TOKEN_OPEN_PAREN,
+    TOKEN_CLOSE_PAREN,
+    TOKEN_SYMBOL,
+    TOKEN_STRING,
+    TOKEN_INTEGER,
+    TOKEN_REAL,
+    TOKEN_TRUE,
+    TOKEN_FALSE
+  };
+
+  Lexer(std::istream& stream);
+  ~Lexer();
+
+  TokenType getNextToken();
+  const char* getString() const
+  { return token_string; }
+  int getLineNumber() const
+  { return linenumber; }
+
+private:
+  enum {
+    MAX_TOKEN_LENGTH = 16384,
+    BUFFER_SIZE = 1024
+  };
+
+  inline void nextChar();
+
+  std::istream& stream;
+  bool eof;
+  int linenumber;
+  char buffer[BUFFER_SIZE+1];
+  char* bufend;
+  char* c;
+  char token_string[MAX_TOKEN_LENGTH + 1];
+  int token_length;
+};
+
+} // end of namespace lisp
+
+#endif
diff --git a/src/lisp/lisp.cpp b/src/lisp/lisp.cpp
new file mode 100644 (file)
index 0000000..f5d8864
--- /dev/null
@@ -0,0 +1,94 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "lisp.hpp"
+
+namespace lisp
+{
+
+Lisp::Lisp(LispType newtype)
+  : type(newtype)
+{
+}
+
+Lisp::~Lisp()
+{
+  // resources should be on parser obstack, so no need to delete anything
+}
+
+const Lisp*
+Lisp::get_lisp(const char* name) const
+{
+  for(const Lisp* p = this; p != 0; p = p->get_cdr()) {
+    const Lisp* child = p->get_car();
+    if(!child || child->get_type() != TYPE_CONS)
+      continue;
+    const Lisp* childname = child->get_car();
+    if(!childname)
+      continue;
+    std::string childName;
+    if(!childname->get(childName))
+      continue;
+    if(childName == name) {
+      return child->get_cdr();
+    }
+  }
+
+  return 0;
+}
+
+void
+Lisp::print(int indent) const
+{
+  for(int i = 0; i < indent; ++i)
+    printf(" ");
+
+  if(type == TYPE_CONS) {
+    printf("(\n");
+    const Lisp* lisp = this;
+    while(lisp) {
+      if(lisp->v.cons.car)
+        lisp->v.cons.car->print(indent + 1);
+      lisp = lisp->v.cons.cdr;
+    }
+    for(int i = 0; i < indent; ++i)
+      printf(" ");
+    printf(")");
+  }
+  if(type == TYPE_STRING) {
+    printf("'%s' ", v.string);
+  }
+  if(type == TYPE_INTEGER) {
+    printf("%d", v.integer);
+  }
+  if(type == TYPE_REAL) {
+    printf("%f", v.real);
+  }
+  if(type == TYPE_SYMBOL) {
+    printf("%s ", v.string);
+  }
+  if(type == TYPE_BOOLEAN) {
+    printf("%s ", v.boolean ? "true" : "false");
+  }
+  printf("\n");
+}
+
+} // end of namespace lisp
diff --git a/src/lisp/lisp.hpp b/src/lisp/lisp.hpp
new file mode 100644 (file)
index 0000000..c65b2ea
--- /dev/null
@@ -0,0 +1,200 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LISPREADER_H__
+#define __LISPREADER_H__
+
+#include <string>
+#include <vector>
+#include <assert.h>
+
+namespace lisp
+{
+
+class Lisp
+{
+public:
+  ~Lisp();
+
+  enum LispType {
+    TYPE_CONS,
+    TYPE_SYMBOL,
+    TYPE_INTEGER,
+    TYPE_STRING,
+    TYPE_REAL,
+    TYPE_BOOLEAN
+  };
+
+  LispType get_type() const
+  { return type; }
+
+  const Lisp* get_car() const
+  { return v.cons.car; }
+  const Lisp* get_cdr() const
+  { return v.cons.cdr; }
+
+  bool get(std::string& val) const
+  {
+    if(type != TYPE_STRING && type != TYPE_SYMBOL)
+      return false;
+    val = v.string;
+    return true;
+  }
+
+  std::string get_symbol() const
+  {
+    assert(type == TYPE_SYMBOL);
+    return v.string;
+  }
+
+  std::string get_string() const
+  {
+    assert(type == TYPE_STRING);
+    return v.string;
+  }
+
+  bool get(unsigned int& val) const
+  {
+    if(type != TYPE_INTEGER)
+      return false;
+    val = v.integer;
+    return true;
+  }
+
+  bool get(int& val) const
+  {
+    if(type != TYPE_INTEGER)
+      return false;
+    val = v.integer;
+    return true;
+  }
+
+  int get_int() const
+  {
+    assert(type == TYPE_INTEGER);
+    return v.integer;
+  }
+
+  bool get(float& val) const
+  {
+    if(type != TYPE_REAL) {
+      if(type == TYPE_INTEGER) {
+        val = (float) v.integer;
+        return true;
+      }
+      return false;
+    }
+    val = v.real;
+    return true;
+  }
+
+  float get_float() const
+  {
+    assert(type == TYPE_REAL);
+    return v.real;
+  }
+
+  bool get(bool& val) const
+  {
+    if(type != TYPE_BOOLEAN)
+      return false;
+    val = v.boolean;
+    return true;
+  }
+
+  bool get_bool() const
+  {
+    assert(type == TYPE_BOOLEAN);
+    return v.boolean;
+  }
+
+  /** conveniance functions which traverse the list until a child with a
+   * specified name is found. The value part is then interpreted in a specific
+   * way. The functions return true, if a child was found and could be
+   * interpreted correctly, otherwise false is returned and the variable value
+   * is not changed.
+   * (Please note that searching the lisp structure is O(n) so these functions
+   *  are no good idea for performance critical areas)
+   */
+  template<class T>
+  bool get(const char* name, T& val) const
+  {
+    const Lisp* lisp = get_lisp(name);
+    if(!lisp)
+      return false;
+
+    if(lisp->get_type() != TYPE_CONS)
+      return false;
+    lisp = lisp->get_car();
+    if(!lisp)
+      return false;
+    return lisp->get(val);
+  }
+
+  template<class T>
+  bool get_vector(const char* name, std::vector<T>& vec) const
+  {
+    vec.clear();
+
+    const Lisp* child = get_lisp(name);
+    if(!child)
+      return false;
+
+    for( ; child != 0; child = child->get_cdr()) {
+      T val;
+      if(!child->get_car())
+        continue;
+      if(child->get_car()->get(val)) {
+        vec.push_back(val);
+      }
+    }
+
+    return true;
+  }
+
+  const Lisp* get_lisp(const char* name) const;
+  const Lisp* get_lisp(const std::string& name) const
+  { return get_lisp(name.c_str()); }
+
+  // for debugging
+  void print(int indent = 0) const;
+
+private:
+  friend class Parser;
+  Lisp(LispType newtype);
+
+  LispType type;
+  union
+  {
+    struct
+    {
+      const Lisp* car;
+      const Lisp* cdr;
+    } cons;
+
+    char* string;
+    int integer;
+    bool boolean;
+    float real;
+  } v;
+};
+
+} // end of namespace lisp
+
+#endif
diff --git a/src/lisp/list_iterator.cpp b/src/lisp/list_iterator.cpp
new file mode 100644 (file)
index 0000000..de46fca
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "list_iterator.hpp"
+#include <stdexcept>
+
+namespace lisp
+{
+
+ListIterator::ListIterator(const lisp::Lisp* newlisp)
+  : current_lisp(0), cur(newlisp)
+{
+}
+
+bool
+ListIterator::next()
+{
+  if(cur == 0)
+    return false;
+
+  const lisp::Lisp* child = cur->get_car();
+  if(!child)
+    throw std::runtime_error("child is 0 in list entry");
+  if(child->get_type() != lisp::Lisp::TYPE_CONS)
+    throw std::runtime_error("Expected CONS");
+  const lisp::Lisp* name = child->get_car();
+  if(!name || (
+        name->get_type() != lisp::Lisp::TYPE_SYMBOL
+        && name->get_type() != lisp::Lisp::TYPE_STRING))
+    throw std::runtime_error("Expected symbol");
+  name->get(current_item);
+  current_lisp = child->get_cdr();
+
+  cur = cur->get_cdr();
+  return true;
+}
+
+}
diff --git a/src/lisp/list_iterator.hpp b/src/lisp/list_iterator.hpp
new file mode 100644 (file)
index 0000000..0cb5b54
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LISP_ITERATOR_H__
+#define __LISP_ITERATOR_H__
+
+#include "lisp/lisp.hpp"
+
+namespace lisp
+{
+
+/**
+ * Small and a bit hacky helper class that helps parsing lisp lists where all
+ * entries are lists again themselves
+ */
+class ListIterator
+{
+public:
+  ListIterator(const lisp::Lisp* cur);
+
+  const std::string& item() const
+  { return current_item; }
+  const lisp::Lisp* lisp() const
+  { return current_lisp; }
+  const lisp::Lisp* value() const
+  { return current_lisp->get_car(); }
+  bool next();
+
+private:
+  std::string current_item;
+  const lisp::Lisp* current_lisp;
+  const lisp::Lisp* cur;
+};
+
+}
+
+#endif
diff --git a/src/lisp/parser.cpp b/src/lisp/parser.cpp
new file mode 100644 (file)
index 0000000..a558adc
--- /dev/null
@@ -0,0 +1,212 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <fstream>
+#include <cassert>
+#include <iostream>
+
+#include "tinygettext/tinygettext.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "parser.hpp"
+#include "lisp.hpp"
+#include "obstack/obstackpp.hpp"
+
+#include "gameconfig.hpp"
+
+namespace lisp
+{
+
+Parser::Parser(bool translate)
+  : lexer(0), dictionary_manager(0), dictionary(0)
+{
+  if(translate) {
+    dictionary_manager = new TinyGetText::DictionaryManager();
+    dictionary_manager->set_charset("UTF-8");
+    if (config && (config->locale != "")) dictionary_manager->set_language(config->locale);
+  }
+
+  obstack_init(&obst);
+}
+
+Parser::~Parser()
+{
+  obstack_free(&obst, NULL);
+  delete lexer;
+  delete dictionary_manager;
+}
+
+static std::string dirname(const std::string& filename)
+{
+  std::string::size_type p = filename.find_last_of('/');
+  if(p == std::string::npos)
+    return "";
+
+  return filename.substr(0, p+1);
+}
+
+const Lisp*
+Parser::parse(const std::string& filename)
+{
+  IFileStreambuf ins(filename);
+  std::istream in(&ins);
+
+  if(!in.good()) {
+    std::stringstream msg;
+    msg << "Parser problem: Couldn't open file '" << filename << "'.";
+    throw std::runtime_error(msg.str());
+  }
+
+  if(dictionary_manager) {
+    dictionary_manager->add_directory(dirname(filename));
+    dictionary = & (dictionary_manager->get_dictionary());
+  }
+
+  return parse(in, filename);
+}
+
+const Lisp*
+Parser::parse(std::istream& stream, const std::string& sourcename)
+{
+  delete lexer;
+  lexer = new Lexer(stream);
+
+  this->filename = sourcename;
+  token = lexer->getNextToken();
+
+  Lisp* result = new(obst) Lisp(Lisp::TYPE_CONS);
+  result->v.cons.car = read();
+  result->v.cons.cdr = 0;
+
+  delete lexer;
+  lexer = 0;
+
+  return result;
+}
+
+void
+Parser::parse_error(const char* msg) const
+{
+  std::stringstream emsg;
+  emsg << "Parse Error at '" << filename << "' line " << lexer->getLineNumber()
+         << ": " << msg;
+  throw std::runtime_error(emsg.str());
+}
+
+const Lisp*
+Parser::read()
+{
+  Lisp* result;
+  switch(token) {
+    case Lexer::TOKEN_EOF: {
+         parse_error("Unexpected EOF.");
+    }
+    case Lexer::TOKEN_CLOSE_PAREN: {
+      parse_error("Unexpected ')'.");
+    }
+    case Lexer::TOKEN_OPEN_PAREN: {
+      result = new(obst) Lisp(Lisp::TYPE_CONS);
+
+      token = lexer->getNextToken();
+      if(token == Lexer::TOKEN_CLOSE_PAREN) {
+        result->v.cons.car = 0;
+        result->v.cons.cdr = 0;
+        break;
+      }
+
+      if(token == Lexer::TOKEN_SYMBOL &&
+          strcmp(lexer->getString(), "_") == 0) {
+        // evaluate translation function (_ str) in place here
+        token = lexer->getNextToken();
+        if(token != Lexer::TOKEN_STRING)
+                 parse_error("Expected string after '(_'");
+
+        result = new(obst) Lisp(Lisp::TYPE_STRING);
+        if(dictionary) {
+          std::string translation = dictionary->translate(lexer->getString());
+          result->v.string = new(obst) char[translation.size()+1];
+          memcpy(result->v.string, translation.c_str(), translation.size()+1);
+        } else {
+          size_t len = strlen(lexer->getString()) + 1;
+          result->v.string = new(obst) char[len];
+          memcpy(result->v.string, lexer->getString(), len);
+        }
+        token = lexer->getNextToken();
+        if(token != Lexer::TOKEN_CLOSE_PAREN)
+                 parse_error("Expected ')' after '(_ string'");
+        break;
+      }
+
+      Lisp* cur = result;
+      do {
+        cur->v.cons.car = read();
+        if(token == Lexer::TOKEN_CLOSE_PAREN) {
+          cur->v.cons.cdr = 0;
+          break;
+        }
+        Lisp *newcur = new(obst) Lisp(Lisp::TYPE_CONS);
+        cur->v.cons.cdr = newcur;
+        cur = newcur;
+      } while(1);
+
+      break;
+    }
+    case Lexer::TOKEN_SYMBOL: {
+      result = new(obst) Lisp(Lisp::TYPE_SYMBOL);
+      size_t len = strlen(lexer->getString()) + 1;
+      result->v.string = new(obst) char[len];
+      memcpy(result->v.string, lexer->getString(), len);
+      break;
+    }
+    case Lexer::TOKEN_STRING: {
+      result = new(obst) Lisp(Lisp::TYPE_STRING);
+      size_t len = strlen(lexer->getString()) + 1;
+      result->v.string = new(obst) char[len];
+      memcpy(result->v.string, lexer->getString(), len);
+      break;
+    }
+    case Lexer::TOKEN_INTEGER:
+      result = new(obst) Lisp(Lisp::TYPE_INTEGER);
+      sscanf(lexer->getString(), "%d", &result->v.integer);
+      break;
+    case Lexer::TOKEN_REAL:
+      result = new(obst) Lisp(Lisp::TYPE_REAL);
+      sscanf(lexer->getString(), "%f", &result->v.real);
+      break;
+    case Lexer::TOKEN_TRUE:
+      result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
+      result->v.boolean = true;
+      break;
+    case Lexer::TOKEN_FALSE:
+      result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
+      result->v.boolean = false;
+      break;
+
+    default:
+      // this should never happen
+      assert(false);
+  }
+
+  token = lexer->getNextToken();
+  return result;
+}
+
+} // end of namespace lisp
diff --git a/src/lisp/parser.hpp b/src/lisp/parser.hpp
new file mode 100644 (file)
index 0000000..d4a986e
--- /dev/null
@@ -0,0 +1,71 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LISPPARSER_H__
+#define __LISPPARSER_H__
+
+#include <string>
+#include "lexer.hpp"
+#include "obstack/obstack.h"
+
+namespace TinyGetText {
+class Dictionary;
+class DictionaryManager;
+}
+
+namespace lisp
+{
+
+class Lisp;
+class LispFile;
+
+class Parser
+{
+public:
+  Parser(bool translate = true);
+  ~Parser();
+
+  /**
+   * Parses a lispfile and returns the s-expression structure.
+   * Note that all memory is held by the parser so don't destroy the parser
+   * before you are finished with the lisp tree
+   */
+  const Lisp* parse(const std::string& filename);
+  /**
+   * Same as parse but reads from a generic std::istream. The sourcename is
+   * used for errormessages to indicate the source of the data.
+   */
+  const Lisp* parse(std::istream& stream, const std::string& sourcename);
+
+private:
+  void parse_error(const char* msg) const;
+  const Lisp* read();
+
+  Lexer* lexer;
+  std::string filename;
+  TinyGetText::DictionaryManager* dictionary_manager;
+  TinyGetText::Dictionary* dictionary;
+  Lexer::TokenType token;
+
+  struct obstack obst;
+};
+
+} // end of namespace lisp
+
+#endif
diff --git a/src/lisp/writer.cpp b/src/lisp/writer.cpp
new file mode 100644 (file)
index 0000000..d2c7b1c
--- /dev/null
@@ -0,0 +1,188 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+
+#include "writer.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "log.hpp"
+
+namespace lisp
+{
+
+Writer::Writer(const std::string& filename)
+{
+  out = new OFileStream(filename);
+  out_owned = true;
+  indent_depth = 0;
+  out->precision(10);
+}
+
+Writer::Writer(std::ostream* newout)
+{
+  out = newout;
+  out_owned = false;
+  indent_depth = 0;
+  out->precision(10);
+}
+
+Writer::~Writer()
+{
+  if(lists.size() > 0) {
+    log_warning << "Not all sections closed in lispwriter" << std::endl;
+  }
+  if(out_owned)
+    delete out;
+}
+
+void
+Writer::write_comment(const std::string& comment)
+{
+  *out << "; " << comment << "\n";
+}
+
+void
+Writer::start_list(const std::string& listname, bool string)
+{
+  indent();
+  *out << '(';
+  if(string)
+    write_escaped_string(listname);
+  else
+    *out << listname;
+  *out << '\n';
+  indent_depth += 2;
+
+  lists.push_back(listname);
+}
+
+void
+Writer::end_list(const std::string& listname)
+{
+  if(lists.size() == 0) {
+    log_warning << "Trying to close list '" << listname << "', which is not open" << std::endl;
+    return;
+  }
+  if(lists.back() != listname) {
+    log_warning << "trying to close list '" << listname << "' while list '" << lists.back() << "' is open" << std::endl;
+    return;
+  }
+  lists.pop_back();
+
+  indent_depth -= 2;
+  indent();
+  *out << ")\n";
+}
+
+void
+Writer::write_int(const std::string& name, int value)
+{
+  indent();
+  *out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+Writer::write_float(const std::string& name, float value)
+{
+  indent();
+  *out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+Writer::write_string(const std::string& name, const std::string& value,
+    bool translatable)
+{
+  indent();
+  *out << '(' << name;
+  if(translatable) {
+    *out << " (_ ";
+    write_escaped_string(value);
+    *out << "))\n";
+  } else {
+    *out << " ";
+    write_escaped_string(value);
+    *out << ")\n";
+  }
+}
+
+void
+Writer::write_bool(const std::string& name, bool value)
+{
+  indent();
+  *out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n";
+}
+
+void
+Writer::write_int_vector(const std::string& name,
+    const std::vector<int>& value)
+{
+  indent();
+  *out << '(' << name;
+  for(std::vector<int>::const_iterator i = value.begin(); i != value.end(); ++i)
+    *out << " " << *i;
+  *out << ")\n";
+}
+
+void
+Writer::write_int_vector(const std::string& name,
+    const std::vector<unsigned int>& value)
+{
+  indent();
+  *out << '(' << name;
+  for(std::vector<unsigned int>::const_iterator i = value.begin(); i != value.end(); ++i)
+    *out << " " << *i;
+  *out << ")\n";
+}
+
+void
+Writer::write_float_vector(const std::string& name,
+                           const std::vector<float>& value)
+{
+  indent();
+  *out << '(' << name;
+  for(std::vector<float>::const_iterator i = value.begin(); i != value.end(); ++i)
+    *out << " " << *i;
+  *out << ")\n";
+}
+
+void
+Writer::write_escaped_string(const std::string& str)
+{
+  *out << '"';
+  for(const char* c = str.c_str(); *c != 0; ++c) {
+    if(*c == '\"')
+      *out << "\\\"";
+    else if(*c == '\\')
+      *out << "\\\\";
+    else
+      *out << *c;
+  }
+  *out << '"';
+}
+
+void
+Writer::indent()
+{
+  for(int i = 0; i<indent_depth; ++i)
+    *out << ' ';
+}
+
+} // end of namespace lisp
diff --git a/src/lisp/writer.hpp b/src/lisp/writer.hpp
new file mode 100644 (file)
index 0000000..ba5d327
--- /dev/null
@@ -0,0 +1,65 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_LISPWRITER_H
+#define SUPERTUX_LISPWRITER_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace lisp
+{
+
+  class Writer
+  {
+  public:
+    Writer(const std::string& filename);
+    Writer(std::ostream* out);
+    ~Writer();
+
+    void write_comment(const std::string& comment);
+
+    void start_list(const std::string& listname, bool string = false);
+
+    void write_int(const std::string& name, int value);
+    void write_float(const std::string& name, float value);
+    void write_string(const std::string& name, const std::string& value,
+        bool translatable = false);
+    void write_bool(const std::string& name, bool value);
+    void write_int_vector(const std::string& name, const std::vector<int>& value);
+    void write_int_vector(const std::string& name, const std::vector<unsigned int>& value);
+    void write_float_vector(const std::string& name, const std::vector<float>& value);
+    // add more write-functions when needed...
+
+    void end_list(const std::string& listname);
+
+  private:
+    void write_escaped_string(const std::string& str);
+    void indent();
+
+    std::ostream* out;
+    bool out_owned;
+    int indent_depth;
+    std::vector<std::string> lists;
+  };
+
+} //namespace lisp
+
+#endif //SUPERTUX_LISPWRITER_H
diff --git a/src/log.cpp b/src/log.cpp
new file mode 100644 (file)
index 0000000..ca54b1d
--- /dev/null
@@ -0,0 +1,37 @@
+//  $Id$
+//
+//  SuperTux Debug Helper Functions
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "log.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+std::ostream& operator<<(std::ostream& out, const Vector& vector)
+{
+  out << '[' << vector.x << ',' << vector.y << ']';
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Rect& rect)
+{
+  out << "[" << rect.get_left() << "," << rect.get_top() << "   "
+             << rect.get_right() << "," << rect.get_bottom() << "]";
+  return out;
+}
diff --git a/src/log.hpp b/src/log.hpp
new file mode 100644 (file)
index 0000000..9cd5a62
--- /dev/null
@@ -0,0 +1,87 @@
+//  $Id$
+//
+//  SuperTux Debug Helper Functions
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __SUPERTUX_MSG_H__
+#define __SUPERTUX_MSG_H__
+
+#include <iostream>
+#include <stdio.h>
+
+#include "console.hpp"
+
+#ifdef DEBUG
+
+namespace {
+
+inline std::ostream& log_debug_f(const char* file, int line) {
+  Console::output << "[DEBUG] " << file << ":" << line << " ";
+  return Console::output;
+}
+
+inline std::ostream& log_info_f(const char* file, int line) {
+  Console::output << "[INFO] " << file << ":" << line << " ";
+  return Console::output;
+}
+
+inline std::ostream& log_warning_f(const char* file, int line) {
+  Console::output << "[WARNING] " << file << ":" << line << " ";
+  return Console::output;
+}
+
+inline std::ostream& log_fatal_f(const char* file, int line) {
+  Console::output << "[FATAL] " << file << ":" << line << " ";
+  return Console::output;
+}
+
+}
+
+#define log_debug log_debug_f(__FILE__, __LINE__)
+#define log_info log_info_f(__FILE__, __LINE__)
+#define log_warning log_warning_f(__FILE__, __LINE__)
+#define log_fatal log_fatal_f(__FILE__, __LINE__)
+
+#else
+
+namespace {
+
+inline std::ostream& log_warning_f() {
+  Console::output << "Warning: ";
+  return Console::output;
+}
+
+inline std::ostream& log_fatal_f() {
+  Console::output << "Fatal: ";
+  return Console::output;
+}
+
+}
+
+#define log_debug if (0) std::cerr
+#define log_info std::cout
+#define log_warning log_warning_f()
+#define log_fatal log_fatal_f()
+
+#endif
+
+class Vector;
+std::ostream& operator<< (std::ostream& str, const Vector& vector);
+class Rect;
+std::ostream& operator<< (std::ostream& str, const Rect& rect);
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..cca8615
--- /dev/null
@@ -0,0 +1,630 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+#include <assert.h>
+
+#include "log.hpp"
+#include "main.hpp"
+
+#include <stdexcept>
+#include <sstream>
+#include <ctime>
+#include <cstdlib>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <physfs.h>
+#include <SDL.h>
+#include <SDL_image.h>
+
+#ifdef MACOSX
+namespace supertux_apple {
+#include <CoreFoundation/CoreFoundation.h>
+}
+#endif
+
+#include "gameconfig.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "audio/sound_manager.hpp"
+#include "video/surface.hpp"
+#include "video/texture_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "video/glutil.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "options_menu.hpp"
+#include "mainloop.hpp"
+#include "title.hpp"
+#include "game_session.hpp"
+#include "scripting/level.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "file_system.hpp"
+#include "physfs/physfs_sdl.hpp"
+#include "random_generator.hpp"
+#include "worldmap/worldmap.hpp"
+#include "binreloc/binreloc.h"
+
+namespace { DrawingContext *context_pointer; }
+SDL_Surface *screen;
+JoystickKeyboardController* main_controller = 0;
+TinyGetText::DictionaryManager dictionary_manager;
+
+int SCREEN_WIDTH;
+int SCREEN_HEIGHT;
+
+static void init_config()
+{
+  config = new Config();
+  try {
+    config->load();
+  } catch(std::exception& e) {
+    log_info << "Couldn't load config file: " << e.what() << ", using default settings" << std::endl;
+  }
+}
+
+static void init_tinygettext()
+{
+  dictionary_manager.add_directory("locale");
+  dictionary_manager.set_charset("UTF-8");
+
+  // Config setting "locale" overrides language detection
+  if (config->locale != "") {
+    dictionary_manager.set_language( config->locale );
+  }
+}
+
+static void init_physfs(const char* argv0)
+{
+  if(!PHYSFS_init(argv0)) {
+    std::stringstream msg;
+    msg << "Couldn't initialize physfs: " << PHYSFS_getLastError();
+    throw std::runtime_error(msg.str());
+  }
+
+  // Initialize physfs (this is a slightly modified version of
+  // PHYSFS_setSaneConfig
+  const char* application = "supertux2"; //instead of PACKAGE_NAME so we can coexist with MS1
+  const char* userdir = PHYSFS_getUserDir();
+  const char* dirsep = PHYSFS_getDirSeparator();
+  char* writedir = new char[strlen(userdir) + strlen(application) + 2];
+
+  // Set configuration directory
+  sprintf(writedir, "%s.%s", userdir, application);
+  if(!PHYSFS_setWriteDir(writedir)) {
+    // try to create the directory
+    char* mkdir = new char[strlen(application) + 2];
+    sprintf(mkdir, ".%s", application);
+    if(!PHYSFS_setWriteDir(userdir) || !PHYSFS_mkdir(mkdir)) {
+      std::ostringstream msg;
+      msg << "Failed creating configuration directory '"
+          << writedir << "': " << PHYSFS_getLastError();
+      delete[] writedir;
+      delete[] mkdir;
+      throw std::runtime_error(msg.str());
+    }
+    delete[] mkdir;
+
+    if(!PHYSFS_setWriteDir(writedir)) {
+      std::ostringstream msg;
+      msg << "Failed to use configuration directory '"
+          <<  writedir << "': " << PHYSFS_getLastError();
+      delete[] writedir;
+      throw std::runtime_error(msg.str());
+    }
+  }
+  PHYSFS_addToSearchPath(writedir, 0);
+  delete[] writedir;
+
+  // Search for archives and add them to the search path
+  const char* archiveExt = "zip";
+  char** rc = PHYSFS_enumerateFiles("/");
+  size_t extlen = strlen(archiveExt);
+
+  for(char** i = rc; *i != 0; ++i) {
+    size_t l = strlen(*i);
+    if((l > extlen) && ((*i)[l - extlen - 1] == '.')) {
+      const char* ext = (*i) + (l - extlen);
+      if(strcasecmp(ext, archiveExt) == 0) {
+        const char* d = PHYSFS_getRealDir(*i);
+        char* str = new char[strlen(d) + strlen(dirsep) + l + 1];
+        sprintf(str, "%s%s%s", d, dirsep, *i);
+        PHYSFS_addToSearchPath(str, 1);
+        delete[] str;
+      }
+    }
+  }
+
+  PHYSFS_freeList(rc);
+
+  // when started from source dir...
+  std::string dir = PHYSFS_getBaseDir();
+  dir += "/data";
+  std::string testfname = dir;
+  testfname += "/credits.txt";
+  bool sourcedir = false;
+  FILE* f = fopen(testfname.c_str(), "r");
+  if(f) {
+    fclose(f);
+    if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+      log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+    } else {
+      sourcedir = true;
+    }
+  }
+
+#ifdef MACOSX
+{
+  using namespace supertux_apple;
+
+  // when started from Application file on Mac OS X...
+  char path[PATH_MAX];
+  CFBundleRef mainBundle = CFBundleGetMainBundle();
+  assert(mainBundle != 0);
+  CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
+  assert(mainBundleURL != 0);
+  CFStringRef pathStr = CFURLCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle);
+  assert(pathStr != 0);
+  CFStringGetCString(pathStr, path, PATH_MAX, kCFStringEncodingUTF8);
+  CFRelease(mainBundleURL);
+  CFRelease(pathStr);
+
+  dir = std::string(path) + "/Contents/Resources/data";
+  testfname = dir + "/credits.txt";
+  sourcedir = false;
+  f = fopen(testfname.c_str(), "r");
+  if(f) {
+    fclose(f);
+    if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+      log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+    } else {
+      sourcedir = true;
+    }
+  }
+}
+#endif
+
+#ifdef _WIN32
+  PHYSFS_addToSearchPath(".\\data", 1);
+#endif
+
+  if(!sourcedir) {
+#if defined(APPDATADIR) || defined(ENABLE_BINRELOC)
+    std::string datadir;
+#ifdef ENABLE_BINRELOC
+
+    char* dir;
+    br_init (NULL);
+    dir = br_find_data_dir(APPDATADIR);
+    datadir = dir;
+    free(dir);
+
+#else
+    datadir = APPDATADIR;
+#endif
+    datadir += "/";
+    datadir += application;
+    if(!PHYSFS_addToSearchPath(datadir.c_str(), 1)) {
+      log_warning << "Couldn't add '" << datadir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+    }
+#endif
+  }
+
+  // allow symbolic links
+  PHYSFS_permitSymbolicLinks(1);
+
+  //show search Path
+  char** searchpath = PHYSFS_getSearchPath();
+  for(char** i = searchpath; *i != NULL; i++)
+    log_info << "[" << *i << "] is in the search path" << std::endl;
+  PHYSFS_freeList(searchpath);
+}
+
+static void print_usage(const char* argv0)
+{
+  fprintf(stderr, _("Usage: %s [OPTIONS] [LEVELFILE]\n\n"), argv0);
+  fprintf(stderr,
+          _("Options:\n"
+            "  -f, --fullscreen             Run in fullscreen mode\n"
+            "  -w, --window                 Run in window mode\n"
+            "  -g, --geometry WIDTHxHEIGHT  Run SuperTux in given resolution\n"
+            "  -a, --aspect WIDTH:HEIGHT    Run SuperTux with given aspect ratio\n"
+            "  --disable-sfx                Disable sound effects\n"
+            "  --disable-music              Disable music\n"
+            "  --help                       Show this help message\n"
+            "  --version                    Display SuperTux version and quit\n"
+            "  --console                    Enable ingame scripting console\n"
+            "  --noconsole                  Disable ingame scripting console\n"
+            "  --show-fps                   Display framerate in levels\n"
+            "  --no-show-fps                Do not display framerate in levels\n"
+            "  --record-demo FILE LEVEL     Record a demo to FILE\n"
+            "  --play-demo FILE LEVEL       Play a recorded demo\n"
+            "\n"));
+}
+
+/**
+ * Options that should be evaluated prior to any initializations at all go here
+ */
+static bool pre_parse_commandline(int argc, char** argv)
+{
+  for(int i = 1; i < argc; ++i) {
+    std::string arg = argv[i];
+
+    if(arg == "--version") {
+      std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/**
+ * Options that should be evaluated after config is read go here
+ */
+static bool parse_commandline(int argc, char** argv)
+{
+  for(int i = 1; i < argc; ++i) {
+    std::string arg = argv[i];
+
+    if(arg == "--help") {
+      print_usage(argv[0]);
+      return true;
+    } else if(arg == "--fullscreen" || arg == "-f") {
+      config->use_fullscreen = true;
+    } else if(arg == "--window" || arg == "-w") {
+      config->use_fullscreen = false;
+    } else if(arg == "--geometry" || arg == "-g") {
+      if(i+1 >= argc) {
+        print_usage(argv[0]);
+        throw std::runtime_error("Need to specify a parameter for geometry switch");
+      }
+      if(sscanf(argv[++i], "%dx%d", &config->screenwidth, &config->screenheight)
+         != 2) {
+        print_usage(argv[0]);
+        throw std::runtime_error("Invalid geometry spec, should be WIDTHxHEIGHT");
+      }
+    } else if(arg == "--aspect" || arg == "-a") {
+      if(i+1 >= argc) {
+        print_usage(argv[0]);
+        throw std::runtime_error("Need to specify a parameter for aspect switch");
+      }
+      if(strcasecmp(argv[i+1], "auto") == 0) {
+        i++;
+        config->aspect_ratio = -1;
+      } else {
+        int aspect_width, aspect_height;
+        if(sscanf(argv[++i], "%d:%d", &aspect_width, &aspect_height) != 2) {
+          print_usage(argv[0]);
+          throw std::runtime_error("Invalid aspect spec, should be WIDTH:HEIGHT");
+        }
+        config->aspect_ratio = static_cast<double>(aspect_width) /
+                               static_cast<double>(aspect_height);
+      }
+    } else if(arg == "--show-fps") {
+      config->show_fps = true;
+    } else if(arg == "--no-show-fps") {
+      config->show_fps = false;
+    } else if(arg == "--console") {
+      config->console_enabled = true;
+    } else if(arg == "--noconsole") {
+      config->console_enabled = false;
+    } else if(arg == "--disable-sfx") {
+      config->sound_enabled = false;
+    } else if(arg == "--disable-music") {
+      config->music_enabled = false;
+    } else if(arg == "--play-demo") {
+      if(i+1 >= argc) {
+        print_usage(argv[0]);
+        throw std::runtime_error("Need to specify a demo filename");
+      }
+      config->start_demo = argv[++i];
+    } else if(arg == "--record-demo") {
+      if(i+1 >= argc) {
+        print_usage(argv[0]);
+        throw std::runtime_error("Need to specify a demo filename");
+      }
+      config->record_demo = argv[++i];
+    } else if(arg == "-d") {
+      config->enable_script_debugger = true;
+    } else if(arg[0] != '-') {
+      config->start_level = arg;
+    } else {
+      log_warning << "Unknown option '" << arg << "'. Use --help to see a list of options" << std::endl;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static void init_sdl()
+{
+  if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
+    std::stringstream msg;
+    msg << "Couldn't initialize SDL: " << SDL_GetError();
+    throw std::runtime_error(msg.str());
+  }
+  // just to be sure
+  atexit(SDL_Quit);
+
+  SDL_EnableUNICODE(1);
+
+  // wait 100ms and clear SDL event queue because sometimes we have random
+  // joystick events in the queue on startup...
+  SDL_Delay(100);
+  SDL_Event dummy;
+  while(SDL_PollEvent(&dummy))
+      ;
+}
+
+static void init_rand()
+{
+  config->random_seed = systemRandom.srand(config->random_seed);
+
+  //const char *how = config->random_seed? ", user fixed.": ", from time().";
+  //log_info << "Using random seed " << config->random_seed << how << std::endl;
+}
+
+void init_video()
+{
+  static int desktop_width = 0;
+  static int desktop_height = 0;
+
+/* unfortunately only newer SDLs have these infos */
+#if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION > 2 || (SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL >= 10)
+  /* find which resolution the user normally uses */
+  if(desktop_width == 0) {
+    const SDL_VideoInfo *info = SDL_GetVideoInfo();
+    desktop_width  = info->current_w;
+    desktop_height = info->current_h;
+  }
+#endif
+
+  double aspect_ratio = config->aspect_ratio;
+
+  // try to guess aspect ratio of monitor if needed
+  if (aspect_ratio <= 0) {
+// TODO: commented out because 
+// 1) it tends to guess wrong if widescreen-monitors don't stretch 800x600 to fit, but just display black borders
+// 2) aspect ratios other than 4:3 are largely untested
+/*
+    if(config->use_fullscreen && desktop_width > 0) {
+      aspect_ratio = static_cast<double>(desktop_width) / static_cast<double>(desktop_height);
+    } else {
+*/
+      aspect_ratio = 4.0 / 3.0;
+/*
+    }
+*/
+  }
+
+  // use aspect ratio to calculate logical resolution
+  if (aspect_ratio > 1) {
+    SCREEN_WIDTH  = static_cast<int> (600 * aspect_ratio + 0.5);
+    SCREEN_HEIGHT = 600;
+  } else {
+    SCREEN_WIDTH  = 600;
+    SCREEN_HEIGHT = static_cast<int> (600 * 1/aspect_ratio + 0.5);
+  }
+
+  context_pointer->init_renderer();
+  screen = SDL_GetVideoSurface();
+
+  SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
+
+  // set icon
+  SDL_Surface* icon = IMG_Load_RW(
+      get_physfs_SDLRWops("images/engine/icons/supertux.xpm"), true);
+  if(icon != 0) {
+    SDL_WM_SetIcon(icon, 0);
+    SDL_FreeSurface(icon);
+  }
+#ifdef DEBUG
+  else {
+    log_warning << "Couldn't find icon 'images/engine/icons/supertux.xpm'" << std::endl;
+  }
+#endif
+
+  SDL_ShowCursor(0);
+
+  log_info << (config->use_fullscreen?"fullscreen ":"window ") << SCREEN_WIDTH << "x" << SCREEN_HEIGHT << " Ratio: " << aspect_ratio << "\n";
+}
+
+static void init_audio()
+{
+  sound_manager = new SoundManager();
+
+  sound_manager->enable_sound(config->sound_enabled);
+  sound_manager->enable_music(config->music_enabled);
+}
+
+static void quit_audio()
+{
+  if(sound_manager != NULL) {
+    delete sound_manager;
+    sound_manager = NULL;
+  }
+}
+
+void wait_for_event(float min_delay, float max_delay)
+{
+  assert(min_delay <= max_delay);
+
+  Uint32 min = (Uint32) (min_delay * 1000);
+  Uint32 max = (Uint32) (max_delay * 1000);
+
+  Uint32 ticks = SDL_GetTicks();
+  while(SDL_GetTicks() - ticks < min) {
+    SDL_Delay(10);
+    sound_manager->update();
+  }
+
+  // clear event queue
+  SDL_Event event;
+  while (SDL_PollEvent(&event))
+  {}
+
+  /* Handle events: */
+  bool running = false;
+  ticks = SDL_GetTicks();
+  while(running) {
+    while(SDL_PollEvent(&event)) {
+      switch(event.type) {
+        case SDL_QUIT:
+          main_loop->quit();
+          break;
+        case SDL_KEYDOWN:
+        case SDL_JOYBUTTONDOWN:
+        case SDL_MOUSEBUTTONDOWN:
+          running = false;
+      }
+    }
+    if(SDL_GetTicks() - ticks >= (max - min))
+      running = false;
+    sound_manager->update();
+    SDL_Delay(10);
+  }
+}
+
+#ifdef DEBUG
+static Uint32 last_timelog_ticks = 0;
+static const char* last_timelog_component = 0;
+
+static inline void timelog(const char* component)
+{
+  Uint32 current_ticks = SDL_GetTicks();
+
+  if(last_timelog_component != 0) {
+    log_info << "Component '" << last_timelog_component <<  "' finished after " << (current_ticks - last_timelog_ticks) / 1000.0 << " seconds" << std::endl;
+  }
+
+  last_timelog_ticks = current_ticks;
+  last_timelog_component = component;
+}
+#else
+static inline void timelog(const char* )
+{
+}
+#endif
+
+int main(int argc, char** argv)
+{
+  int result = 0;
+
+#ifndef NO_CATCH
+  try {
+#endif
+
+    if(pre_parse_commandline(argc, argv))
+      return 0;
+
+    Console::instance = new Console();
+    init_physfs(argv[0]);
+    init_sdl();
+
+    timelog("controller");
+    main_controller = new JoystickKeyboardController();
+    timelog("config");
+    init_config();
+    timelog("tinygettext");
+    init_tinygettext();
+    timelog("commandline");
+    if(parse_commandline(argc, argv))
+      return 0;
+    timelog("audio");
+    init_audio();
+    timelog("video");
+    DrawingContext context;
+    context_pointer = &context;
+    init_video();
+    Console::instance->init_graphics();
+    timelog("scripting");
+    Scripting::init_squirrel(config->enable_script_debugger);
+    timelog("resources");
+    load_shared();
+    timelog(0);
+
+    main_loop = new MainLoop();
+    if(config->start_level != "") {
+      // we have a normal path specified at commandline not physfs paths.
+      // So we simply mount that path here...
+      std::string dir = FileSystem::dirname(config->start_level);
+      PHYSFS_addToSearchPath(dir.c_str(), true);
+
+      if(config->start_level.size() > 4 &&
+              config->start_level.compare(config->start_level.size() - 5, 5, ".stwm") == 0) {
+          init_rand();
+          main_loop->push_screen(new WorldMapNS::WorldMap(
+                      FileSystem::basename(config->start_level)));
+      } else {
+        init_rand();//If level uses random eg. for
+        // rain particles before we do this:
+        std::auto_ptr<GameSession> session (
+                new GameSession(FileSystem::basename(config->start_level)));
+
+        config->random_seed =session->get_demo_random_seed(config->start_demo);
+        init_rand();//initialise generator with seed from session
+
+        if(config->start_demo != "")
+          session->play_demo(config->start_demo);
+
+        if(config->record_demo != "")
+          session->record_demo(config->record_demo);
+        main_loop->push_screen(session.release());
+      }
+    } else {
+      init_rand();
+      main_loop->push_screen(new TitleScreen());
+    }
+
+    //init_rand(); PAK: this call might subsume the above 3, but I'm chicken!
+    main_loop->run(context);
+#ifndef NO_CATCH
+  } catch(std::exception& e) {
+    log_fatal << "Unexpected exception: " << e.what() << std::endl;
+    result = 1;
+  } catch(...) {
+    log_fatal << "Unexpected exception" << std::endl;
+    result = 1;
+  }
+#endif
+
+  delete main_loop;
+  main_loop = NULL;
+
+  unload_shared();
+  quit_audio();
+
+  if(config)
+    config->save();
+  delete config;
+  config = NULL;
+  delete main_controller;
+  main_controller = NULL;
+  delete Console::instance;
+  Console::instance = NULL;
+  Scripting::exit_squirrel();
+  delete texture_manager;
+  texture_manager = NULL;
+  SDL_Quit();
+  PHYSFS_deinit();
+
+  return result;
+}
diff --git a/src/main.hpp b/src/main.hpp
new file mode 100644 (file)
index 0000000..e64b01f
--- /dev/null
@@ -0,0 +1,35 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+void init_video();
+void wait_for_event(float min_delay, float max_delay);
+
+/// The width of the display (this is a logical value, not the physical value)
+extern int SCREEN_WIDTH;
+/// The height of the display (this is a logical value, not the physical value)
+extern int SCREEN_HEIGHT;
+
+// global variables
+class JoystickKeyboardController;
+extern JoystickKeyboardController* main_controller;
+
+#endif
diff --git a/src/mainloop.cpp b/src/mainloop.cpp
new file mode 100644 (file)
index 0000000..e5db024
--- /dev/null
@@ -0,0 +1,283 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "mainloop.hpp"
+
+#include <stdlib.h>
+#include <SDL.h>
+#include "video/drawing_context.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "gui/menu.hpp"
+#include "audio/sound_manager.hpp"
+#include "scripting/time_scheduler.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "gameconfig.hpp"
+#include "main.hpp"
+#include "resources.hpp"
+#include "screen.hpp"
+#include "screen_fade.hpp"
+#include "timer.hpp"
+#include "player_status.hpp"
+#include "random_generator.hpp"
+
+// the engine will be run with a logical framerate of 64fps.
+// We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
+// binary fraction...
+static const float LOGICAL_FPS = 64.0;
+/** ticks (as returned from SDL_GetTicks) per frame */
+static const Uint32 TICKS_PER_FRAME = (Uint32) (1000.0 / LOGICAL_FPS);
+/** don't skip more than every 2nd frame */
+static const int MAX_FRAME_SKIP = 2;
+
+MainLoop* main_loop = NULL;
+
+MainLoop::MainLoop()
+  : speed(1.0), nextpop(false), nextpush(false), fps(0), screenshot_requested(false)
+{
+  using namespace Scripting;
+  TimeScheduler::instance = new TimeScheduler();
+}
+
+MainLoop::~MainLoop()
+{
+  using namespace Scripting;
+  delete TimeScheduler::instance;
+  TimeScheduler::instance = NULL;
+
+  for(std::vector<Screen*>::iterator i = screen_stack.begin();
+      i != screen_stack.end(); ++i) {
+    delete *i;
+  }
+}
+
+void
+MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade)
+{
+  this->next_screen.reset(screen);
+  this->screen_fade.reset(screen_fade);
+  nextpush = !nextpop;
+  nextpop = false;
+  speed = 1.0;
+}
+
+void
+MainLoop::exit_screen(ScreenFade* screen_fade)
+{
+  next_screen.reset(NULL);
+  this->screen_fade.reset(screen_fade);
+  nextpop = true;
+  nextpush = false;
+}
+
+void
+MainLoop::set_screen_fade(ScreenFade* screen_fade)
+{
+  this->screen_fade.reset(screen_fade);
+}
+
+void
+MainLoop::quit(ScreenFade* screen_fade)
+{
+  for(std::vector<Screen*>::iterator i = screen_stack.begin();
+          i != screen_stack.end(); ++i)
+    delete *i;
+  screen_stack.clear();
+
+  exit_screen(screen_fade);
+}
+
+void
+MainLoop::set_speed(float speed)
+{
+  this->speed = speed;
+}
+
+float
+MainLoop::get_speed() const
+{
+  return speed;
+}
+
+void
+MainLoop::draw_fps(DrawingContext& context, float fps_fps)
+{
+  char str[60];
+  snprintf(str, sizeof(str), "%3.1f", fps_fps);
+  const char* fpstext = "FPS";
+  context.draw_text(white_text, fpstext, Vector(SCREEN_WIDTH - white_text->get_text_width(fpstext) - gold_text->get_text_width(" 99999") - BORDER_X, BORDER_Y + 20), ALIGN_LEFT, LAYER_HUD);
+  context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), ALIGN_RIGHT, LAYER_HUD);
+}
+
+void
+MainLoop::draw(DrawingContext& context)
+{
+  static Uint32 fps_ticks = SDL_GetTicks();
+  static int frame_count = 0;
+
+  current_screen->draw(context);
+  if(Menu::current() != NULL)
+    Menu::current()->draw(context);
+  if(screen_fade.get() != NULL)
+    screen_fade->draw(context);
+  Console::instance->draw(context);
+
+  if(config->show_fps)
+    draw_fps(context, fps);
+
+  // if a screenshot was requested, pass request on to drawing_context
+  if (screenshot_requested) {
+    context.take_screenshot();
+    screenshot_requested = false;
+  }
+  context.do_drawing();
+
+  /* Calculate frames per second */
+  if(config->show_fps)
+  {
+    ++frame_count;
+
+    if(SDL_GetTicks() - fps_ticks >= 500)
+    {
+      fps = (float) frame_count / .5;
+      frame_count = 0;
+      fps_ticks = SDL_GetTicks();
+    }
+  }
+}
+
+void
+MainLoop::update_gamelogic(float elapsed_time)
+{
+  Scripting::update_debugger();
+  Scripting::TimeScheduler::instance->update(game_time);
+  current_screen->update(elapsed_time);
+  if(screen_fade.get() != NULL)
+    screen_fade->update(elapsed_time);
+  Console::instance->update(elapsed_time);
+}
+
+void
+MainLoop::process_events()
+{
+  main_controller->update();
+  SDL_Event event;
+  while(SDL_PollEvent(&event)) {
+    main_controller->process_event(event);
+    if(Menu::current() != NULL)
+      Menu::current()->event(event);
+    if(event.type == SDL_QUIT)
+      quit();
+    else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F11) {
+      config->use_fullscreen = !config->use_fullscreen;
+      init_video();
+    }
+    else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
+      take_screenshot();
+    }
+  }
+}
+
+void
+MainLoop::handle_screen_switch()
+{
+  while( (next_screen.get() != NULL || nextpop) &&
+      (screen_fade.get() == NULL || screen_fade->done())) {
+    if(current_screen.get() != NULL) {
+      current_screen->leave();
+    }
+
+    if(nextpop) {
+      if(screen_stack.empty()) {
+        running = false;
+        break;
+      }
+      next_screen.reset(screen_stack.back());
+      screen_stack.pop_back();
+    }
+    if(nextpush && current_screen.get() != NULL) {
+      screen_stack.push_back(current_screen.release());
+    }
+
+    nextpush = false;
+    nextpop = false;
+    speed = 1.0;
+    if(next_screen.get() != NULL)
+      next_screen->setup();
+    current_screen.reset(next_screen.release());
+    screen_fade.reset(NULL);
+
+    waiting_threads.wakeup();
+  }
+}
+
+void
+MainLoop::run(DrawingContext &context)
+{
+  Uint32 last_ticks = 0;
+  Uint32 elapsed_ticks = 0;
+
+  running = true;
+  while(running) {
+
+    handle_screen_switch();
+    if(!running || current_screen.get() == NULL)
+      break;
+
+    Uint32 ticks = SDL_GetTicks();
+    elapsed_ticks += ticks - last_ticks;
+    last_ticks = ticks;
+
+    if (elapsed_ticks > TICKS_PER_FRAME*4) {
+      // when the game loads up or levels are switched the
+      // elapsed_ticks grows extremly large, so we just ignore those
+      // large time jumps
+      elapsed_ticks = 0;
+    }
+
+    int frames = 0;
+
+    if (elapsed_ticks > TICKS_PER_FRAME) {
+      while(elapsed_ticks > TICKS_PER_FRAME && frames < MAX_FRAME_SKIP) {
+        elapsed_ticks -= TICKS_PER_FRAME;
+        float timestep = 1.0 / LOGICAL_FPS;
+        real_time += timestep;
+        timestep *= speed;
+        game_time += timestep;
+
+        process_events();
+        update_gamelogic(timestep);
+        frames += 1;
+      }
+
+      draw(context);
+    }
+
+    sound_manager->update();
+
+    SDL_Delay(0);
+  }
+}
+
+void 
+MainLoop::take_screenshot()
+{
+  screenshot_requested = true;
+}
+
diff --git a/src/mainloop.hpp b/src/mainloop.hpp
new file mode 100644 (file)
index 0000000..bd03372
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __MAINLOOP_HPP__
+#define __MAINLOOP_HPP__
+
+#include <memory>
+#include <vector>
+#include "scripting/thread_queue.hpp"
+
+class Screen;
+class Console;
+class ScreenFade;
+class DrawingContext;
+
+class MainLoop
+{
+public:
+  MainLoop();
+  ~MainLoop();
+
+  void run(DrawingContext &context);
+  void exit_screen(ScreenFade* fade = NULL);
+  void quit(ScreenFade* fade = NULL);
+  void set_speed(float speed);
+  float get_speed() const;
+
+  /**
+   * requests that a screenshot be taken after the next frame has been rendered
+   */
+  void take_screenshot();
+
+  // push new screen on screen_stack
+  void push_screen(Screen* screen, ScreenFade* fade = NULL);
+  void set_screen_fade(ScreenFade* fade);
+
+  /// threads that wait for a screenswitch
+  Scripting::ThreadQueue waiting_threads;
+
+private:
+  void draw_fps(DrawingContext& context, float fps);
+  void draw(DrawingContext& context);
+  void update_gamelogic(float elapsed_time);
+  void process_events();
+  void handle_screen_switch();
+
+  bool running;
+  float speed;
+  bool nextpop;
+  bool nextpush;
+  /// measured fps
+  float fps;
+  std::auto_ptr<Screen> next_screen;
+  std::auto_ptr<Screen> current_screen;
+  std::auto_ptr<Console> console;
+  std::auto_ptr<ScreenFade> screen_fade;
+  std::vector<Screen*> screen_stack;
+  bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
+};
+
+extern MainLoop* main_loop;
+
+#endif
diff --git a/src/math/aatriangle.hpp b/src/math/aatriangle.hpp
new file mode 100644 (file)
index 0000000..15a0db6
--- /dev/null
@@ -0,0 +1,67 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __AATRIANGLE_H__
+#define __AATRIANGLE_H__
+
+#include "rect.hpp"
+
+/**
+ * An axis aligned triangle (ie. a triangle where 2 sides are parallel to the x-
+ * and y-axis.
+ */
+class AATriangle : public Rect
+{
+public:
+  /** Directions:
+   *
+   *    SOUTHEWEST    NORTHEAST   SOUTHEAST    NORTHWEST
+   *    *      or      *---*   or      *    or *---*
+   *    | \             \  |         / |       |  /
+   *    |  \             \ |        /  |       | /
+   *    *---*              *       *---*       *
+   *
+   * Deform flags: (see docs/aatriangletypes.png for details)
+   */
+  enum Direction {
+    SOUTHWEST = 0,
+    NORTHEAST,
+    SOUTHEAST,
+    NORTHWEST,
+    DIRECTION_MASK = 0x0003,
+    DEFORM1 = 0x0010,
+    DEFORM2 = 0x0020,
+    DEFORM3 = 0x0030,
+    DEFORM4 = 0x0040,
+    DEFORM_MASK = 0x0070
+  };
+
+  AATriangle()
+    : dir(SOUTHWEST)
+  {
+  }
+  AATriangle(const Vector& v1, const Vector& v2, int newdir)
+    : Rect(v1, v2), dir(newdir)
+  {
+  }
+
+  int dir;
+};
+
+#endif
diff --git a/src/math/rect.hpp b/src/math/rect.hpp
new file mode 100644 (file)
index 0000000..2c30258
--- /dev/null
@@ -0,0 +1,121 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __RECT_H__
+#define __RECT_H__
+
+#include <assert.h>
+#include "vector.hpp"
+
+/** This class represents a rectangle.
+ * (Implementation Note) We're using upper left and lower right point instead of
+ * upper left and width/height here, because that makes the collision dectection
+ * a little bit efficienter.
+ */
+class Rect
+{
+public:
+  Rect()
+  { }
+
+  Rect(const Vector& np1, const Vector& np2)
+    : p1(np1), p2(np2)
+  {
+  }
+
+  Rect(float x1, float y1, float x2, float y2)
+    : p1(x1, y1), p2(x2, y2)
+  {
+    assert(p1.x <= p2.x && p1.y <= p2.y);
+  }
+
+  float get_left() const
+  { return p1.x; }
+
+  float get_right() const
+  { return p2.x; }
+
+  float get_top() const
+  { return p1.y; }
+
+  float get_bottom() const
+  { return p2.y; }
+
+  float get_width() const
+  { return p2.x - p1.x; }
+
+  float get_height() const
+  { return p2.y - p1.y; }
+
+  Vector get_middle() const
+  { return Vector((p1.x+p2.x)/2, (p1.y+p2.y)/2); }
+
+  void set_pos(const Vector& v)
+  {
+    move(v-p1);
+  }
+
+  void set_height(float height)
+  {
+    p2.y = p1.y + height;
+  }
+  void set_width(float width)
+  {
+    p2.x = p1.x + width;
+  }
+  void set_size(float width, float height)
+  {
+    set_width(width);
+    set_height(height);
+  }
+  Vector get_size()
+  {
+    return Vector(get_width(), get_height());
+  }
+
+  void move(const Vector& v)
+  {
+    p1 += v;
+    p2 += v;
+  }
+
+  bool contains(const Vector& v) const
+  {
+    return v.x >= p1.x && v.y >= p1.y && v.x < p2.x && v.y < p2.y;
+  }
+  bool contains(const Rect& other) const
+  {
+    if(p1.x >= other.p2.x || other.p1.x >= p2.x)
+      return false;
+    if(p1.y >= other.p2.y || other.p1.y >= p2.y)
+      return false;
+
+    return true;
+  }
+
+  // leave these 2 public to safe the headaches of set/get functions for such
+  // simple things :)
+
+  /// upper left edge
+  Vector p1;
+  /// lower right edge
+  Vector p2;
+};
+
+#endif
diff --git a/src/math/vector.cpp b/src/math/vector.cpp
new file mode 100644 (file)
index 0000000..d7df1b3
--- /dev/null
@@ -0,0 +1,34 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <cmath>
+
+#include "math/vector.hpp"
+
+Vector Vector::unit() const
+{
+  return *this / norm();
+}
+
+float Vector::norm() const
+{
+  return sqrt(x*x + y*y);
+}
diff --git a/src/math/vector.hpp b/src/math/vector.hpp
new file mode 100644 (file)
index 0000000..817bb4d
--- /dev/null
@@ -0,0 +1,122 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_VECTOR_H
+#define SUPERTUX_VECTOR_H
+
+/** Simple two dimensional vector. */
+class Vector
+{
+public:
+  Vector(float nx, float ny)
+      : x(nx), y(ny)
+  { }
+  Vector(const Vector& other)
+      : x(other.x), y(other.y)
+  { }
+  Vector()
+      : x(0), y(0)
+  { }
+
+  bool operator ==(const Vector& other) const
+    {
+      return x == other.x && y == other.y;
+    }
+
+  bool operator !=(const Vector& other) const
+    {
+      return !(x == other.x && y == other.y);
+    }
+
+  const Vector& operator=(const Vector& other)
+  {
+    x = other.x;
+    y = other.y;
+    return *this;
+  }
+
+  Vector operator+(const Vector& other) const
+    {
+      return Vector(x + other.x, y + other.y);
+    }
+
+  Vector operator-(const Vector& other) const
+    {
+      return Vector(x - other.x, y - other.y);
+    }
+
+  Vector operator*(float s) const
+    {
+      return Vector(x * s, y * s);
+    }
+
+  Vector operator/(float s) const
+    {
+      return Vector(x / s, y / s);
+    }
+
+  Vector operator-() const
+    {
+      return Vector(-x, -y);
+    }
+
+  const Vector& operator +=(const Vector& other)
+  {
+    x += other.x;
+    y += other.y;
+    return *this;
+  }
+
+  const Vector& operator -=(const Vector& other)
+  {
+    x -= other.x;
+    y -= other.y;
+    return *this;
+  }
+
+  const Vector& operator *=(float val)
+  {
+    x *= val;
+    y *= val;
+    return *this;
+  }
+
+  const Vector& operator /=(float val)
+  {
+    x /= val;
+    y /= val;
+    return *this;
+  }
+
+  /// Scalar product of 2 vectors
+  float operator*(const Vector& other) const
+    {
+      return x*other.x + y*other.y;
+    }
+
+  float norm() const;
+  Vector unit() const;
+
+  // ... add the other operators as needed, I'm too lazy now ...
+
+  float x, y; // leave this public, get/set methods just give me headaches
+  // for such simple stuff :)
+};
+
+#endif
diff --git a/src/moving_object.cpp b/src/moving_object.cpp
new file mode 100644 (file)
index 0000000..f71e909
--- /dev/null
@@ -0,0 +1,30 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "moving_object.hpp"
+
+MovingObject::MovingObject()
+{
+  group = COLGROUP_MOVING;
+}
+
+MovingObject::~MovingObject()
+{
+}
diff --git a/src/moving_object.hpp b/src/moving_object.hpp
new file mode 100644 (file)
index 0000000..69d36a3
--- /dev/null
@@ -0,0 +1,192 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_MOVING_OBJECT_H
+#define SUPERTUX_MOVING_OBJECT_H
+
+#include <stdint.h>
+
+#include "game_object.hpp"
+#include "collision_hit.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class Sector;
+class CollisionGrid;
+
+enum CollisionGroup {
+  /** Objects in DISABLED group are not tested for collisions */
+  COLGROUP_DISABLED = 0,
+  /**
+   * "default" is moving object. MovingObjects get tested against all other
+   * objects and against other movingobjects
+   */
+  COLGROUP_MOVING,
+  /**
+   * a Moving object, that is not tested against other MovingObjects (or other
+   * MovingOnlyStatic objects), but is tested against all other objects.
+   */
+  COLGROUP_MOVING_ONLY_STATIC,
+  /** TODO write docu :-/ */
+  COLGROUP_MOVING_STATIC,
+  /**
+   * Doesn't move and isn't explicitely checked for collisions with other
+   * objects (but other objects might check with this)
+   * The difference to COLGROUP_TOUCHABLE is that we can do multiple
+   * collision response tests in a row which is needed for static object
+   * that tux walks on. The results for collisions with STATIC objects
+   * are also sorted by time (so that the first hit gets handled first).
+   *
+   * Use this for static obstacles
+   */
+  COLGROUP_STATIC,
+  /**
+   * Isn't explicitely checked for collisions with other objects. But other
+   * objects might check with this object.
+   * Difference to COLGROUP_STATIC is that collisions with this object are
+   * only tested once and collision response is typically not handled
+   *
+   * Use this for touchable things like spikes/areas or collectibles like
+   * coins
+   */
+  COLGROUP_TOUCHABLE,
+
+  /**
+   * Should be used for tilemaps
+   */
+  COLGROUP_TILEMAP
+};
+
+/**
+ * Base class for all dynamic/moving game objects. This class contains things
+ * for handling the bounding boxes and collision feedback.
+ */
+class MovingObject : public GameObject
+{
+public:
+  MovingObject();
+  virtual ~MovingObject();
+
+  /** this function is called when the object collided with something solid */
+  virtual void collision_solid(const CollisionHit& hit)
+  {
+    (void) hit;
+  }
+  /**
+   * when 2 objects collided, we will first call the pre_collision_check
+   * functions of both objects that can decide on how to react to the collision.
+   */
+  virtual bool collides(GameObject& other, const CollisionHit& hit)
+  {
+    (void) other;
+    (void) hit;
+    return true;
+  }
+  /** this function is called when the object collided with any other object */
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit) = 0;
+  /** called when tiles with special attributes have been touched */
+  virtual void collision_tile(uint32_t tile_attributes)
+  {
+    (void) tile_attributes;
+  }
+
+  const Vector& get_pos() const
+  {
+    return bbox.p1;
+  }
+
+  /** returns the bounding box of the Object */
+  const Rect& get_bbox() const
+  {
+    return bbox;
+  }
+
+  const Vector& get_movement() const
+  {
+    return movement;
+  }
+
+  /** places the moving object at a specific position. Be carefull when
+   * using this function. There are no collision detection checks performed
+   * here so bad things could happen.
+   */
+  virtual void set_pos(const Vector& pos)
+  {
+    dest.move(pos-get_pos());
+    bbox.set_pos(pos);
+  }
+
+  /**
+   * sets the moving object's bbox to a specific width. Be careful when
+   * using this function. There are no collision detection checks performed
+   * here so bad things could happen.
+   */
+  virtual void set_width(float w)
+  {
+    dest.set_width(w);
+    bbox.set_width(w);
+  }
+
+  /**
+   * sets the moving object's bbox to a specific size. Be careful when
+   * using this function. There are no collision detection checks performed
+   * here so bad things could happen.
+   */
+  virtual void set_size(float w, float h)
+  {
+    dest.set_size(w, h);
+    bbox.set_size(w, h);
+  }
+
+  CollisionGroup get_group() const
+  {
+    return group;
+  }
+
+protected:
+  friend class Sector;
+  friend class CollisionGrid;
+  friend class Platform;
+
+  void set_group(CollisionGroup group)
+  {
+    this->group = group;
+  }
+
+  /** The bounding box of the object (as used for collision detection, this
+   * isn't necessarily the bounding box for graphics)
+   */
+  Rect bbox;
+  /** The movement that will happen till next frame
+   */
+  Vector movement;
+  /** The collision group */
+  CollisionGroup group;
+
+private:
+  /**
+   * this is only here for internal collision detection use (don't touch this
+   * from outside collision detection code)
+   *
+   * This field holds the currently anticipated destination of the object
+   * during collision detection
+   */
+  Rect dest;
+};
+
+#endif
diff --git a/src/object/ambient_sound.cpp b/src/object/ambient_sound.cpp
new file mode 100644 (file)
index 0000000..5d3a792
--- /dev/null
@@ -0,0 +1,243 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include <stdexcept>
+#include <iostream>
+#include <limits>
+
+#include "ambient_sound.hpp"
+#include "object_factory.hpp"
+#include "lisp/lisp.hpp"
+#include "sector.hpp"
+#include "audio/sound_manager.hpp"
+#include "audio/sound_source.hpp"
+#include "log.hpp"
+#include "scripting/squirrel_util.hpp"
+
+AmbientSound::AmbientSound(const lisp::Lisp& lisp)
+{
+  name="";
+  position.x = 0;
+  position.y = 0;
+
+  dimension.x = 0;
+  dimension.y = 0;
+
+  distance_factor = 0;
+  distance_bias = 0;
+  maximumvolume = 1;
+  sample = "";
+  currentvolume = 0;
+
+  if (!(lisp.get("x", position.x)&&lisp.get("y", position.y))) {
+    log_warning << "No Position in ambient_sound" << std::endl;
+  }
+
+  lisp.get("name" , name);
+  lisp.get("width" , dimension.x);
+  lisp.get("height", dimension.y);
+
+  lisp.get("distance_factor",distance_factor);
+  lisp.get("distance_bias"  ,distance_bias  );
+  lisp.get("sample"         ,sample         );
+  lisp.get("volume"         ,maximumvolume  );
+
+  // set dimension to zero if smaller than 64, which is default size in flexlay
+
+  if ((dimension.x <= 64) || (dimension.y <= 64)) {
+    dimension.x = 0;
+    dimension.y = 0;
+  }
+
+  // square all distances (saves us a sqrt later)
+
+  distance_bias*=distance_bias;
+  distance_factor*=distance_factor;
+
+  // set default silence_distance
+
+  if (distance_factor == 0)
+         silence_distance = std::numeric_limits<float>::max();
+  else
+    silence_distance = 1/distance_factor;
+
+  lisp.get("silence_distance",silence_distance);
+
+  sound_source = 0; // not playing at the beginning
+  latency=0;
+}
+
+AmbientSound::AmbientSound(Vector pos, float factor, float bias, float vol, std::string file)
+{
+  position.x=pos.x;
+  position.y=pos.y;
+
+  dimension.x=0;
+  dimension.y=0;
+
+  distance_factor=factor*factor;
+  distance_bias=bias*bias;
+  maximumvolume=vol;
+  sample=file;
+
+  // set default silence_distance
+
+  if (distance_factor == 0)
+         silence_distance = std::numeric_limits<float>::max();
+  else
+    silence_distance = 1/distance_factor;
+
+  sound_source = 0; // not playing at the beginning
+  latency=0;
+}
+
+AmbientSound::~AmbientSound() {
+  stop_playing();
+}
+
+void
+AmbientSound::hit(Player& )
+{
+}
+
+void
+AmbientSound::stop_playing() {
+  delete sound_source;
+  sound_source = 0;
+}
+
+void
+AmbientSound::start_playing()
+{
+  try {
+    sound_source = sound_manager->create_sound_source(sample);
+    if(!sound_source)
+      throw std::runtime_error("file not found");
+
+    sound_source->set_gain(0);
+    sound_source->set_looping(true);
+    currentvolume=targetvolume=1e-20f;
+    sound_source->play();
+  } catch(std::exception& e) {
+    log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl;
+    delete sound_source;
+    sound_source = 0;
+    remove_me();
+  }
+}
+
+void
+AmbientSound::update(float deltat)
+{
+  if (latency-- <= 0) {
+    float px,py;
+    float rx,ry;
+
+    // Player position
+    px=Sector::current()->player->get_pos().x;
+    py=Sector::current()->player->get_pos().y;
+
+    // Relate to which point in the area
+    rx=px<position.x?position.x:
+      (px<position.x+dimension.x?px:position.x+dimension.x);
+    ry=py<position.y?position.y:
+      (py<position.y+dimension.y?py:position.y+dimension.y);
+
+    // calculate square of distance
+    float sqrdistance=(px-rx)*(px-rx)+(py-ry)*(py-ry);
+    sqrdistance-=distance_bias;
+
+    // inside the bias: full volume (distance 0)
+    if (sqrdistance<0)
+      sqrdistance=0;
+
+    // calculate target volume - will never become 0
+    targetvolume=1/(1+sqrdistance*distance_factor);
+    float rise=targetvolume/currentvolume;
+
+    // rise/fall half life?
+    currentvolume*=pow(rise,deltat*10);
+    currentvolume += 1e-6f; // volume is at least 1e-6 (0 would never rise)
+
+    if (sound_source != 0) {
+
+      // set the volume
+      sound_source->set_gain(currentvolume*maximumvolume);
+
+      if (sqrdistance>=silence_distance && currentvolume<1e-3)
+       stop_playing();
+      latency=0;
+    } else {
+      if (sqrdistance<silence_distance) {
+       start_playing();
+       latency=0;
+      }
+      else // set a reasonable latency
+       latency=(int)(0.001/distance_factor);
+      //(int)(10*((sqrdistance-silence_distance)/silence_distance));
+    }
+  }
+
+  // heuristically measured "good" latency maximum
+
+  //  if (latency>0.001/distance_factor)
+  // latency=
+}
+
+void
+AmbientSound::draw(DrawingContext &)
+{
+}
+
+void
+AmbientSound::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  Scripting::AmbientSound* interface = static_cast<Scripting::AmbientSound*> (this);
+  expose_object(vm, table_idx, interface, name, false);
+}
+
+void
+AmbientSound::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+AmbientSound::set_pos(float x, float y)
+{
+  position.x = x;
+  position.y = y;
+}
+
+float
+AmbientSound::get_pos_x() const
+{
+  return position.x;
+}
+
+float
+AmbientSound::get_pos_y() const
+{
+  return position.y;
+}
+
+IMPLEMENT_FACTORY(AmbientSound, "ambient_sound");
diff --git a/src/object/ambient_sound.hpp b/src/object/ambient_sound.hpp
new file mode 100644 (file)
index 0000000..a7a14bc
--- /dev/null
@@ -0,0 +1,109 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+/**
+ *  Ambient Sound Source, gamma version. Features:
+ *
+ *  - "rounded rectancle" geometry with position, dimension and
+ *    "rounding radius" (extending in all directions) of a 100%
+ *    volume area, adjustable maximum volume, inverse square
+ *    falloff outside area.
+ *
+ *  - degenerates gracefully to a disc for dimension=0
+ *
+ *  - parameters:
+ *
+ *    x, y               position
+ *    width, height      dimension
+ *    distance_factor    high = steep fallofff
+ *    distance_bias      high = big "100% disc"
+ *    silence_distance   defaults reasonably.
+ *    sample             sample to be played back in loop mode
+ *
+ *      basti_
+ */
+
+#ifndef __AMBIENT_SOUND_H__
+#define __AMBIENT_SOUND_H__
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "script_interface.hpp"
+#include "scripting/ambient_sound.hpp"
+
+class SoundSource;
+
+class AmbientSound : public GameObject, public ScriptInterface, public Scripting::AmbientSound
+{
+public:
+  AmbientSound(const lisp::Lisp& lisp);
+  AmbientSound(Vector pos, float factor, float bias, float vol, std::string file);
+  ~AmbientSound();
+
+  void set_pos(Vector newpos)
+  {
+    position=newpos;
+  }
+  const Vector get_pos() const
+  {
+    return position;
+  }
+
+  /**
+   * @name Scriptable Methods
+   * @{
+   */
+  void set_pos(float x, float y);
+  float get_pos_x() const;
+  float get_pos_y() const;
+  /**
+   * @}
+   */
+
+protected:
+  virtual void hit(Player& player);
+  virtual void update(float time);
+  virtual void draw(DrawingContext&);
+  virtual void start_playing();
+  virtual void stop_playing();
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+private:
+  std::string name; /**< user-defined name for use in scripts or empty string if not scriptable */
+  Vector position;
+  Vector dimension;
+
+  std::string sample;
+  SoundSource* sound_source;
+  int latency;
+
+  float distance_factor;  /// distance scaling
+  float distance_bias;    /// 100% volume disc radius
+  float silence_distance; /// not implemented yet
+
+  float maximumvolume; /// maximum volume
+  float targetvolume;  /// how loud we want to be
+  float currentvolume; /// how loud we are
+
+  float * volume_ptr; /// this will be used by the volume adjustment effect.
+};
+
+#endif
diff --git a/src/object/anchor_point.cpp b/src/object/anchor_point.cpp
new file mode 100644 (file)
index 0000000..4c42edb
--- /dev/null
@@ -0,0 +1,169 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include "anchor_point.hpp"
+#include "math/rect.hpp"
+#include "log.hpp"
+
+std::string anchor_point_to_string(AnchorPoint point)
+{
+  switch(point) {
+    case ANCHOR_TOP_LEFT:
+      return "topleft";
+    case ANCHOR_TOP:
+      return "top";
+    case ANCHOR_TOP_RIGHT:
+      return "topright";
+    case ANCHOR_LEFT:
+      return "left";
+    case ANCHOR_MIDDLE:
+      return "middle";
+    case ANCHOR_RIGHT:
+      return "right";
+    case ANCHOR_BOTTOM_LEFT:
+      return "bottomleft";
+    case ANCHOR_BOTTOM:
+      return "bottom";
+    case ANCHOR_BOTTOM_RIGHT:
+      return "bottomright";
+    default:
+      throw std::runtime_error("Invalid anchor point");
+  }
+}
+
+AnchorPoint string_to_anchor_point(const std::string& str)
+{
+  if(str == "topleft")
+    return ANCHOR_TOP_LEFT;
+  else if(str == "top")
+    return ANCHOR_TOP;
+  else if(str == "topright")
+    return ANCHOR_TOP_RIGHT;
+  else if(str == "left")
+    return ANCHOR_LEFT;
+  else if(str == "middle")
+    return ANCHOR_MIDDLE;
+  else if(str == "right")
+    return ANCHOR_RIGHT;
+  else if(str == "bottomleft")
+    return ANCHOR_BOTTOM_LEFT;
+  else if(str == "bottom")
+    return ANCHOR_BOTTOM;
+  else if(str == "bottomright")
+    return ANCHOR_BOTTOM_RIGHT;
+
+  std::ostringstream msg;
+  msg << "Unknown anchor '" << str << "'";
+  throw std::runtime_error(msg.str());
+}
+
+Vector get_anchor_pos(const Rect& rect, AnchorPoint point)
+{
+  Vector result;
+
+  switch(point & ANCHOR_V_MASK) {
+    case ANCHOR_LEFT:
+      result.x = rect.get_left();
+      break;
+    case ANCHOR_MIDDLE:
+      result.x = rect.get_left() + (rect.get_right() - rect.get_left()) / 2.0;
+      break;
+    case ANCHOR_RIGHT:
+      result.x = rect.get_right();
+      break;
+    default:
+#ifdef DEBUG
+      throw std::runtime_error("Invalid anchor point found");
+#endif
+      log_warning << "Invalid anchor point found" << std::endl;
+      result.x = rect.get_left();
+      break;
+  }
+
+  switch(point & ANCHOR_H_MASK) {
+    case ANCHOR_TOP:
+      result.y = rect.get_top();
+      break;
+    case ANCHOR_MIDDLE:
+      result.y = rect.get_top() + (rect.get_bottom() - rect.get_top()) / 2.0;
+      break;
+    case ANCHOR_BOTTOM:
+      result.y = rect.get_bottom();
+      break;
+    default:
+#ifdef DEBUG
+      throw std::runtime_error("Invalid anchor point found");
+#endif
+      log_warning << "Invalid anchor point found" << std::endl;
+      result.y = rect.get_top();
+      break;
+  }
+
+  return result;
+}
+
+Vector get_anchor_pos(const Rect& destrect, float width, float height,
+                      AnchorPoint point)
+{
+  Vector result;
+
+  switch(point & ANCHOR_V_MASK) {
+    case ANCHOR_LEFT:
+      result.x = destrect.get_left();
+      break;
+    case ANCHOR_MIDDLE:
+      result.x = destrect.get_middle().x - width/2.0;
+      break;
+    case ANCHOR_RIGHT:
+      result.x = destrect.get_right() - width;
+      break;
+    default:
+#ifdef DEBUG
+      throw std::runtime_error("Invalid anchor point found");
+#endif
+      log_warning << "Invalid anchor point found" << std::endl;
+      result.x = destrect.get_left();
+      break;
+  }
+
+  switch(point & ANCHOR_H_MASK) {
+    case ANCHOR_TOP:
+      result.y = destrect.get_top();
+      break;
+    case ANCHOR_MIDDLE:
+      result.y = destrect.get_middle().y - height/2.0;
+      break;
+    case ANCHOR_BOTTOM:
+      result.y = destrect.get_bottom() - height;
+      break;
+    default:
+#ifdef DEBUG
+      throw std::runtime_error("Invalid anchor point found");
+#endif
+      log_warning << "Invalid anchor point found" << std::endl;
+      result.y = destrect.get_top();
+      break;
+  }
+
+  return result;
+}
diff --git a/src/object/anchor_point.hpp b/src/object/anchor_point.hpp
new file mode 100644 (file)
index 0000000..5b8baee
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ANCHOR_POINT_HPP__
+#define __ANCHOR_POINT_HPP__
+
+#include <string>
+#include "math/vector.hpp"
+
+class Rect;
+
+enum AnchorPoint {
+  ANCHOR_H_MASK = 0x00f0,
+  ANCHOR_TOP    = 0x0010,
+  ANCHOR_BOTTOM = 0x0020,
+  ANCHOR_V_MASK = 0x000f,
+  ANCHOR_LEFT   = 0x0001,
+  ANCHOR_RIGHT  = 0x0002,
+  ANCHOR_MIDDLE = 0x0000,
+
+  ANCHOR_TOP_LEFT = ANCHOR_TOP | ANCHOR_LEFT,
+  ANCHOR_TOP_RIGHT = ANCHOR_TOP | ANCHOR_RIGHT,
+  ANCHOR_BOTTOM_LEFT = ANCHOR_BOTTOM | ANCHOR_LEFT,
+  ANCHOR_BOTTOM_RIGHT = ANCHOR_BOTTOM | ANCHOR_RIGHT,
+};
+
+std::string anchor_point_to_string(AnchorPoint point);
+AnchorPoint string_to_anchor_point(const std::string& str);
+Vector get_anchor_pos(const Rect& rect, AnchorPoint point);
+Vector get_anchor_pos(const Rect& destrect, float width, float height,
+                      AnchorPoint point);
+
+#endif
diff --git a/src/object/background.cpp b/src/object/background.cpp
new file mode 100644 (file)
index 0000000..f60f184
--- /dev/null
@@ -0,0 +1,132 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include "background.hpp"
+#include "camera.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+Background::Background()
+  : layer(LAYER_BACKGROUND0)
+{
+}
+
+Background::Background(const lisp::Lisp& reader)
+  : layer(LAYER_BACKGROUND0)
+{
+  // read position, defaults to (0,0)
+  float px = 0;
+  float py = 0;
+  reader.get("x", px);
+  reader.get("y", py);
+  this->pos = Vector(px,py);
+
+  speed = 1.0;
+  speed_y = 1.0;
+
+  reader.get("layer", layer);
+  if(!reader.get("image", imagefile) || !reader.get("speed", speed))
+    throw std::runtime_error("Must specify image and speed for background");
+
+  set_image(imagefile, speed);
+  reader.get("speed-y", speed_y);
+  if (reader.get("image-top", imagefile_top)) {
+    image_top.reset(new Surface(imagefile_top));
+  }
+  if (reader.get("image-bottom", imagefile_bottom)) {
+    image_bottom.reset(new Surface(imagefile_bottom));
+  }
+}
+
+Background::~Background()
+{
+}
+
+void
+Background::write(lisp::Writer& writer)
+{
+  writer.start_list("background");
+
+  if (image_top.get() != NULL)
+    writer.write_string("image-top", imagefile_top);
+
+  writer.write_string("image", imagefile);
+  if (image_bottom.get() != NULL)
+    writer.write_string("image-bottom", imagefile_bottom);
+
+  writer.write_float("speed", speed);
+  writer.write_float("speed-y", speed_y);
+  writer.write_int("layer", layer);
+
+  writer.end_list("background");
+}
+
+void
+Background::update(float)
+{
+}
+
+void
+Background::set_image(const std::string& name, float speed)
+{
+  this->imagefile = name;
+  this->speed = speed;
+
+  image.reset(new Surface(name));
+}
+
+void
+Background::draw(DrawingContext& context)
+{
+  if(image.get() == NULL)
+    return;
+
+  int w = (int) image->get_width();
+  int h = (int) image->get_height();
+  int sx = int(pos.x-context.get_translation().x * speed) % w - w;
+  int sy = int(pos.y-context.get_translation().y * speed_y) % h - h;
+  int center_image_py = int(pos.y-context.get_translation().y * speed_y);
+  int bottom_image_py = int(pos.y-context.get_translation().y * speed_y) + h;
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+  for(int x = sx; x < SCREEN_WIDTH; x += w) {
+    for(int y = sy; y < SCREEN_HEIGHT; y += h) {
+      if (image_top.get() != NULL && (y < center_image_py)) {
+        context.draw_surface(image_top.get(), Vector(x, y), layer);
+        continue;
+      }
+      if (image_bottom.get() != NULL && (y >= bottom_image_py)) {
+        context.draw_surface(image_bottom.get(), Vector(x, y), layer);
+        continue;
+      }
+      context.draw_surface(image.get(), Vector(x, y), layer);
+    }
+  }
+  context.pop_transform();
+}
+
+IMPLEMENT_FACTORY(Background, "background");
diff --git a/src/object/background.hpp b/src/object/background.hpp
new file mode 100644 (file)
index 0000000..3321c92
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_BACKGROUND_H
+#define SUPERTUX_BACKGROUND_H
+
+#include <memory>
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+
+class DisplayManager;
+
+namespace lisp {
+class Lisp;
+}
+
+class Background : public GameObject, public Serializable
+{
+public:
+  Background();
+  Background(const lisp::Lisp& reader);
+  virtual ~Background();
+
+  virtual void write(lisp::Writer& writer);
+
+  void set_image(const std::string& name, float bkgd_speed);
+
+  std::string get_image() const
+  { return imagefile; }
+  float get_speed() const
+  { return speed; }
+
+  virtual void update(float elapsed_time);
+
+  virtual void draw(DrawingContext& context);
+
+private:
+  int layer;
+  std::string imagefile_top;
+  std::string imagefile;
+  std::string imagefile_bottom;
+  Vector pos; /**< coordinates of upper-left corner of image */
+  float speed; /**< scroll-speed in horizontal direction */
+  float speed_y; /**< scroll-speed in vertical direction */
+  std::auto_ptr<Surface> image_top; /**< image to draw above pos */
+  std::auto_ptr<Surface> image; /**< image to draw, anchored at pos */
+  std::auto_ptr<Surface> image_bottom; /**< image to draw below pos+screenheight */
+};
+
+#endif /*SUPERTUX_BACKGROUND_H*/
diff --git a/src/object/bicycle_platform.cpp b/src/object/bicycle_platform.cpp
new file mode 100644 (file)
index 0000000..4014049
--- /dev/null
@@ -0,0 +1,128 @@
+//  $Id$
+//
+//  SuperTux - BicyclePlatform
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "bicycle_platform.hpp"
+
+#include <math.h>
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "object/portable.hpp"
+
+BicyclePlatform::BicyclePlatform(const lisp::Lisp& reader)
+       : MovingSprite(reader, LAYER_OBJECTS, COLGROUP_STATIC), 
+       master(0), slave(0), radius(128), angle(0), angular_speed(0), momentum(0)
+{
+  center = get_pos();
+}
+
+BicyclePlatform::BicyclePlatform(BicyclePlatform* master)
+       : MovingSprite(*master), 
+       master(master), slave(this), center(master->center), radius(master->radius), angle(master->angle + M_PI), angular_speed(0), momentum(0)
+{
+  set_pos(get_pos() + Vector(master->get_bbox().get_width(), 0));
+  master->master = master;
+  master->slave = this;
+}
+
+BicyclePlatform::~BicyclePlatform() {
+  if ((this == master) && (master)) {
+    slave->master = 0;
+    slave->slave = 0;
+  }
+  if ((master) && (this == slave)) {
+    master->master = 0;
+    master->slave = 0;
+  }
+  master = 0;
+  slave = 0;
+}
+
+HitResponse
+BicyclePlatform::collision(GameObject& other, const CollisionHit& )
+{
+
+  // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this:
+  MovingObject* mo = dynamic_cast<MovingObject*>(&other);
+  if (!mo) return FORCE_MOVE;
+  if ((mo->get_bbox().p2.y) > (get_bbox().p1.y + 2)) return FORCE_MOVE;
+
+  Player* pl = dynamic_cast<Player*>(mo);
+  if (pl) {
+    if (pl->is_big()) momentum += 1;
+    Portable* po = pl->get_grabbed_object();
+    MovingObject* pomo = dynamic_cast<MovingObject*>(po);
+    if (contacts.insert(pomo).second) momentum += 1;
+  }
+
+  if (contacts.insert(&other).second) momentum += 1;
+  return FORCE_MOVE;
+}
+
+void
+BicyclePlatform::update(float elapsed_time)
+{
+  if (!slave) {
+    Sector::current()->add_object(new BicyclePlatform(this));
+    return;
+  }
+  if (!master) {
+    return;
+  }
+  if (this == slave) {
+    angle = master->angle + M_PI;
+    while (angle < 0) { angle += 2*M_PI; } 
+    while (angle > 2*M_PI) { angle -= 2*M_PI; } 
+    Vector dest = center + Vector(cosf(angle), sinf(angle)) * radius - (bbox.get_size() * 0.5);
+    movement = dest - get_pos();
+  }
+  if (this == master) {
+    float momentum_diff = momentum - slave->momentum;
+    contacts.clear(); momentum = 0;
+    slave->contacts.clear(); slave->momentum = 0;
+
+    float angular_momentum = cosf(angle) * momentum_diff;
+
+    angular_speed += (angular_momentum * elapsed_time) * M_PI;
+    angular_speed *= 1 - elapsed_time * 0.2;
+    angle += angular_speed * elapsed_time;
+    while (angle < 0) { angle += 2*M_PI; } 
+    while (angle > 2*M_PI) { angle -= 2*M_PI; } 
+    angular_speed = std::min(std::max(angular_speed, static_cast<float>(-128*M_PI*elapsed_time)), static_cast<float>(128*M_PI*elapsed_time));
+    Vector dest = center + Vector(cosf(angle), sinf(angle)) * radius - (bbox.get_size() * 0.5);
+    movement = dest - get_pos();
+
+    center += Vector(angular_speed, 0) * elapsed_time * 32;
+    slave->center += Vector(angular_speed, 0) * elapsed_time * 32;
+
+  }
+}
+
+IMPLEMENT_FACTORY(BicyclePlatform, "bicycle-platform");
+
diff --git a/src/object/bicycle_platform.hpp b/src/object/bicycle_platform.hpp
new file mode 100644 (file)
index 0000000..2dcd779
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux - BicyclePlatform
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BICYCLE_PLATFORM_H__
+#define __BICYCLE_PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include <set>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+
+/**
+ * Used to construct a pair of bicycle platforms: If one is pushed down, the other one rises
+ */
+class BicyclePlatform : public MovingSprite
+{
+public:
+  BicyclePlatform(const lisp::Lisp& reader);
+  BicyclePlatform(BicyclePlatform* master);
+  virtual ~BicyclePlatform();
+
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+  virtual void update(float elapsed_time);
+
+protected:
+  BicyclePlatform* master; /**< pointer to BicyclePlatform that does movement calculation */
+  BicyclePlatform* slave; /**< pointer to BicyclePlatform that reacts to master platform's movement calculation */
+  Vector center; /**< pivot point */
+  float radius; /**< radius of circle */
+  float angle; /**< current angle */
+  float angular_speed; /**< angular speed in rad per second */
+  std::set<GameObject*> contacts; /**< objects that are currently pushing on the platform */
+  float momentum; /** angular momentum in rad per second per second*/
+
+};
+
+#endif
diff --git a/src/object/block.cpp b/src/object/block.cpp
new file mode 100644 (file)
index 0000000..fd6e2fc
--- /dev/null
@@ -0,0 +1,405 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "block.hpp"
+#include "log.hpp"
+
+#include <stdexcept>
+
+#include "resources.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "gameobjs.hpp"
+#include "portable.hpp"
+#include "specialriser.hpp"
+#include "growup.hpp"
+#include "flower.hpp"
+#include "oneup.hpp"
+#include "star.hpp"
+#include "player_status.hpp"
+#include "badguy/badguy.hpp"
+#include "coin.hpp"
+#include "object_factory.hpp"
+#include "lisp/list_iterator.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+
+static const float BOUNCY_BRICK_MAX_OFFSET = 8;
+static const float BOUNCY_BRICK_SPEED = 90;
+static const float EPSILON = .0001f;
+
+Block::Block(Sprite* newsprite)
+  : sprite(newsprite), bouncing(false), breaking(false), bounce_dir(0), bounce_offset(0)
+{
+  bbox.set_size(32, 32.1f);
+  set_group(COLGROUP_STATIC);
+  sound_manager->preload("sounds/upgrade.wav");
+  sound_manager->preload("sounds/brick.wav");
+}
+
+Block::~Block()
+{
+  delete sprite;
+}
+
+HitResponse
+Block::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player) {
+    if(player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+      hit(*player);
+    }
+  }
+
+  // only interact with other objects if...
+  //   1) we are bouncing
+  // and
+  //   2) the object is not portable (either never or not currently)
+  Portable* portable = dynamic_cast<Portable*> (&other);
+  if(bouncing && (portable == 0 || (!portable->is_portable()))) {
+
+    // Badguys get killed
+    BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+    if(badguy) {
+      badguy->kill_fall();
+    }
+
+    // Coins get collected
+    Coin* coin = dynamic_cast<Coin*> (&other);
+    if(coin) {
+      coin->collect();
+    }
+
+  }
+
+  return SOLID;
+}
+
+void
+Block::update(float elapsed_time)
+{
+  if(!bouncing)
+    return;
+
+  float offset = original_y - get_pos().y;
+  if(offset > BOUNCY_BRICK_MAX_OFFSET) {
+    bounce_dir = BOUNCY_BRICK_SPEED;
+    movement = Vector(0, bounce_dir * elapsed_time);
+    if(breaking){
+      break_me();
+    }
+  } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) {
+    movement = Vector(0, offset);
+    bounce_dir = 0;
+    bouncing = false;
+  } else {
+    movement = Vector(0, bounce_dir * elapsed_time);
+  }
+}
+
+void
+Block::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), LAYER_OBJECTS+1);
+}
+
+void
+Block::start_bounce()
+{
+  original_y = bbox.p1.y;
+  bouncing = true;
+  bounce_dir = -BOUNCY_BRICK_SPEED;
+  bounce_offset = 0;
+}
+
+void
+Block::start_break()
+{
+  start_bounce();
+  breaking = true;
+}
+
+//---------------------------------------------------------------------------
+
+BonusBlock::BonusBlock(const Vector& pos, int data)
+  : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")), object(0)
+{
+  bbox.set_pos(pos);
+  sprite->set_action("normal");
+  switch(data) {
+    case 1: contents = CONTENT_COIN; break;
+    case 2: contents = CONTENT_FIREGROW; break;
+    case 3: contents = CONTENT_STAR; break;
+    case 4: contents = CONTENT_1UP; break;
+    case 5: contents = CONTENT_ICEGROW; break;
+    default:
+      log_warning << "Invalid box contents" << std::endl;
+      contents = CONTENT_COIN;
+      break;
+  }
+}
+
+BonusBlock::BonusBlock(const lisp::Lisp& lisp)
+  : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite"))
+{
+  Vector pos;
+
+  contents = CONTENT_COIN;
+  lisp::ListIterator iter(&lisp);
+  while(iter.next()) {
+    const std::string& token = iter.item();
+    if(token == "x") {
+      iter.value()->get(pos.x);
+    } else if(token == "y") {
+      iter.value()->get(pos.y);
+    } else if(token == "contents") {
+      std::string contentstring;
+      iter.value()->get(contentstring);
+      if(contentstring == "coin") {
+        contents = CONTENT_COIN;
+      } else if(contentstring == "firegrow") {
+        contents = CONTENT_FIREGROW;
+      } else if(contentstring == "icegrow") {
+        contents = CONTENT_ICEGROW;
+      } else if(contentstring == "star") {
+        contents = CONTENT_STAR;
+      } else if(contentstring == "1up") {
+        contents = CONTENT_1UP;
+      } else if(contentstring == "custom") {
+        contents = CONTENT_CUSTOM;
+      } else {
+        log_warning << "Invalid box contents '" << contentstring << "'" << std::endl;
+      }
+    } else {
+      if(contents == CONTENT_CUSTOM) {
+        GameObject* game_object = create_object(token, *(iter.lisp()));
+        object = dynamic_cast<MovingObject*> (game_object);
+        if(object == 0)
+          throw std::runtime_error(
+            "Only MovingObjects are allowed inside BonusBlocks");
+      } else {
+        log_warning << "Invalid element '" << token << "' in bonusblock" << std::endl;
+      }
+    }
+  }
+
+  if(contents == CONTENT_CUSTOM && object == 0)
+    throw std::runtime_error("Need to specify content object for custom block");
+
+  bbox.set_pos(pos);
+}
+
+BonusBlock::~BonusBlock()
+{
+  delete object;
+}
+
+void
+BonusBlock::hit(Player& )
+{
+  try_open();
+}
+
+HitResponse
+BonusBlock::collision(GameObject& other, const CollisionHit& hit){
+    BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+    if(badguy) {
+      // hit contains no information for collisions with blocks.
+      // Badguy's bottom has to be below the top of the bonusblock
+      // +7 is required to slide over one tile gaps.
+      if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0) ){
+        try_open();
+      }
+    }
+    Portable* portable = dynamic_cast<Portable*> (&other);
+    if(portable) {
+      MovingObject* moving = dynamic_cast<MovingObject*> (&other);
+      if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+        try_open();
+      }
+    }
+    return Block::collision(other, hit);
+}
+
+void
+BonusBlock::try_open()
+{
+  if(sprite->get_action() == "empty") {
+    sound_manager->play("sounds/brick.wav");
+    return;
+  }
+
+  Sector* sector = Sector::current();
+  assert(sector);
+  assert(sector->player);
+  Player& player = *(sector->player);
+  Direction direction = (player.get_bbox().get_middle().x > get_bbox().get_middle().x) ? LEFT : RIGHT;
+
+  switch(contents) {
+    case CONTENT_COIN:
+      Sector::current()->add_object(new BouncyCoin(get_pos()));
+      player.get_status()->add_coins(1);
+      Sector::current()->get_level()->stats.coins++;
+      break;
+
+    case CONTENT_FIREGROW:
+      if(player.get_status()->bonus == NO_BONUS) {
+        SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
+        sector->add_object(riser);
+      } else {
+        SpecialRiser* riser = new SpecialRiser(
+            get_pos(), new Flower(FIRE_BONUS));
+        sector->add_object(riser);
+      }
+      sound_manager->play("sounds/upgrade.wav");
+      break;
+
+    case CONTENT_ICEGROW:
+      if(player.get_status()->bonus == NO_BONUS) {
+        SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
+        sector->add_object(riser);
+      } else {
+        SpecialRiser* riser = new SpecialRiser(
+            get_pos(), new Flower(ICE_BONUS));
+        sector->add_object(riser);
+      }
+      sound_manager->play("sounds/upgrade.wav");
+      break;
+
+    case CONTENT_STAR:
+      sector->add_object(new Star(get_pos() + Vector(0, -32), direction));
+      break;
+
+    case CONTENT_1UP:
+      sector->add_object(new OneUp(get_pos(), direction));
+      break;
+
+    case CONTENT_CUSTOM:
+      SpecialRiser* riser = new SpecialRiser(get_pos(), object);
+      object = 0;
+      sector->add_object(riser);
+      sound_manager->play("sounds/upgrade.wav");
+      break;
+  }
+
+  start_bounce();
+  sprite->set_action("empty");
+}
+
+void
+Block::break_me()
+{
+  Sector* sector = Sector::current();
+  sector->add_object(
+      new BrokenBrick(new Sprite(*sprite), get_pos(), Vector(-100, -400)));
+  sector->add_object(
+      new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(0, 16),
+        Vector(-150, -300)));
+  sector->add_object(
+      new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 0),
+        Vector(100, -400)));
+  sector->add_object(
+      new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 16),
+        Vector(150, -300)));
+  remove_me();
+}
+
+IMPLEMENT_FACTORY(BonusBlock, "bonusblock");
+
+//---------------------------------------------------------------------------
+
+Brick::Brick(const Vector& pos, int data)
+  : Block(sprite_manager->create("images/objects/bonus_block/brick.sprite")), breakable(false),
+    coin_counter(0)
+{
+  bbox.set_pos(pos);
+  if(data == 1)
+    coin_counter = 5;
+  else
+    breakable = true;
+}
+
+void
+Brick::hit(Player& )
+{
+  if(sprite->get_action() == "empty")
+    return;
+
+  try_break(true);
+}
+
+HitResponse
+Brick::collision(GameObject& other, const CollisionHit& hit){
+    BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+    if(badguy) {
+      // hit contains no information for collisions with blocks.
+      // Badguy's bottom has to be below the top of the brick
+      // +7 is required to slide over one tile gaps.
+      if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0 ) ){
+        try_break(false);
+      }
+    }
+    Portable* portable = dynamic_cast<Portable*> (&other);
+    if(portable) {
+      MovingObject* moving = dynamic_cast<MovingObject*> (&other);
+      if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+        try_break();
+      }
+    }
+   return Block::collision(other, hit);
+}
+
+void
+Brick::try_break(bool playerhit)
+{
+  if(sprite->get_action() == "empty")
+    return;
+
+  sound_manager->play("sounds/brick.wav");
+  Sector* sector = Sector::current();
+  Player& player = *(sector->player);
+  if(coin_counter > 0) {
+    sector->add_object(new BouncyCoin(get_pos()));
+    coin_counter--;
+    player.get_status()->add_coins(1);
+    if(coin_counter == 0)
+      sprite->set_action("empty");
+    start_bounce();
+  } else if(breakable) {
+    if(playerhit){
+      if(player.is_big()){
+        start_break();
+        return;
+      } else {
+        start_bounce();
+        return;
+      }
+    }
+   break_me();
+  }
+}
+
+//IMPLEMENT_FACTORY(Brick, "brick");
diff --git a/src/object/block.hpp b/src/object/block.hpp
new file mode 100644 (file)
index 0000000..94f87fb
--- /dev/null
@@ -0,0 +1,99 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BLOCK_H__
+#define __BLOCK_H__
+
+#include "moving_object.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+class Player;
+
+class Block : public MovingObject
+{
+public:
+  Block(Sprite* sprite = 0);
+  ~Block();
+
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+protected:
+  friend class FlipLevelTransformer;
+
+  virtual void hit(Player& player) = 0;
+  void start_bounce();
+  void start_break();
+  void break_me();
+
+  Sprite* sprite;
+  bool bouncing;
+  bool breaking;
+  float bounce_dir;
+  float bounce_offset;
+  float original_y;
+
+};
+
+class BonusBlock : public Block
+{
+public:
+  BonusBlock(const Vector& pos, int data);
+  BonusBlock(const lisp::Lisp& lisp);
+  virtual ~BonusBlock();
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  void try_open();
+
+  enum Contents {
+    CONTENT_COIN,
+    CONTENT_FIREGROW,
+    CONTENT_ICEGROW,
+    CONTENT_STAR,
+    CONTENT_1UP,
+    CONTENT_CUSTOM
+  };
+
+  Contents contents;
+protected:
+  virtual void hit(Player& player);
+
+private:
+  MovingObject* object;
+};
+
+class Brick : public Block
+{
+public:
+  Brick(const Vector& pos, int data);
+
+  void try_break(bool playerhit = false);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+  virtual void hit(Player& player);
+
+private:
+  bool breakable;
+  int coin_counter;
+};
+
+#endif
diff --git a/src/object/bullet.cpp b/src/object/bullet.cpp
new file mode 100644 (file)
index 0000000..c761735
--- /dev/null
@@ -0,0 +1,108 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include "bullet.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "badguy/badguy.hpp"
+#include "main.hpp"
+
+namespace {
+  const float BULLET_XM = 600;
+  const float BULLET_STARTING_YM = 0;
+}
+
+Bullet::Bullet(const Vector& pos, float xm, int dir, BonusType type)
+  : life_count(3), type(type)
+{
+  float speed = dir == RIGHT ? BULLET_XM : -BULLET_XM;
+  physic.set_velocity_x(speed + xm);
+
+  if(type == FIRE_BONUS) {
+    sprite.reset(sprite_manager->create("images/objects/bullets/firebullet.sprite"));
+  } else if(type == ICE_BONUS) {
+    life_count = 10;
+    sprite.reset(sprite_manager->create("images/objects/bullets/icebullet.sprite"));
+  }
+
+  bbox.set_pos(pos);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Bullet::~Bullet()
+{
+}
+
+void
+Bullet::update(float elapsed_time)
+{
+  // remove bullet when it's offscreen
+  float scroll_x =
+    Sector::current()->camera->get_translation().x;
+  float scroll_y =
+    Sector::current()->camera->get_translation().y;
+  if (get_pos().x < scroll_x ||
+      get_pos().x > scroll_x + SCREEN_WIDTH ||
+//     get_pos().y < scroll_y ||
+      get_pos().y > scroll_y + SCREEN_HEIGHT ||
+      life_count <= 0) {
+    remove_me();
+    return;
+  }
+
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+Bullet::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+void
+Bullet::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom) {
+    physic.set_velocity_y(-physic.get_velocity_y());
+    life_count--;
+  } else if(hit.left || hit.right) {
+    if(type == ICE_BONUS) {
+      physic.set_velocity_x(-physic.get_velocity_x());
+      life_count--;
+    } else
+      remove_me();
+  }
+}
+
+void
+Bullet::ricochet(GameObject& , const CollisionHit& hit)
+{
+  collision_solid(hit);
+}
+
+HitResponse
+Bullet::collision(GameObject& , const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
diff --git a/src/object/bullet.hpp b/src/object/bullet.hpp
new file mode 100644 (file)
index 0000000..c3464d7
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __BULLET_H__
+#define __BULLET_H__
+
+#include "moving_object.hpp"
+#include "physic.hpp"
+#include "sprite/sprite.hpp"
+#include "player_status.hpp"
+
+class Bullet : public MovingObject, private UsesPhysic
+{
+public:
+  Bullet(const Vector& pos, float xm, int dir, BonusType type);
+  ~Bullet();
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  /**
+   * Makes bullet bounce off an object (that got hit).
+   * To be called by the collision handler of that object.
+   * Note that the @c hit parameter is filled in as perceived by the object, not by the bullet.
+   */
+  void ricochet(GameObject& other, const CollisionHit& hit);
+
+  BonusType get_type()
+  {
+    return type;
+  }
+
+private:
+  int life_count;
+  std::auto_ptr<Sprite> sprite;
+  BonusType type;
+};
+
+#endif
diff --git a/src/object/camera.cpp b/src/object/camera.cpp
new file mode 100644 (file)
index 0000000..42dd1aa
--- /dev/null
@@ -0,0 +1,538 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <cmath>
+
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/parser.hpp"
+#include "scripting/camera.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "camera.hpp"
+#include "player.hpp"
+#include "tilemap.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "main.hpp"
+#include "object_factory.hpp"
+#include "log.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+
+class CameraConfig
+{
+public:
+  // 0 = No, 1 = Fix, 2 = Mario/Yoshi, 3 = Kirby
+  int ymode;
+  // as above, 4 = super metroid like
+  int xmode;
+  float kirby_rectsize_x;
+  float kirby_rectsize_y;
+  // where to fix the player (used for Yoshi and Fix camera)
+  float target_y;
+  float target_x;
+  // maximum scrolling speed in Y direction
+  float max_speed_y;
+  float max_speed_x;
+  // factor to dynamically increase max_speed_x based on player speed
+  float dynamic_max_speed_x;
+
+  // time the player has to face into the other direction before we assume a
+  // changed direction
+  float dirchange_time;
+  // edge_x
+  float edge_x;
+  // when too change from noscroll mode back to lookahead left/right mode
+  // set to <= 0 to disable noscroll mode
+  float sensitive_x;
+
+  float clamp_y;
+  float clamp_x;
+
+  float dynamic_speed_sm;
+
+  CameraConfig() {
+    xmode = 1;
+    ymode = 1;
+    target_x = .5f;
+    target_y = 2.f/3.f;
+    max_speed_y = 140;
+    max_speed_x = 130;
+    clamp_x = 1.f/6.f;
+    clamp_y = 1.f/6.f;
+    kirby_rectsize_x = 0.2f;
+    kirby_rectsize_y = 0.34f;
+    edge_x = 1.f/3.f;
+    sensitive_x = 1.f/4.f;
+    dynamic_max_speed_x = 1.0;
+    dirchange_time = 0.2f;
+    dynamic_speed_sm = 1.0f;
+  }
+
+  void load(const std::string& filename)
+  {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(filename);
+    const lisp::Lisp* camconfig = root->get_lisp("camera-config");
+    if(camconfig == NULL)
+      throw std::runtime_error("file is not a camera config file.");
+
+    camconfig->get("xmode", xmode);
+    camconfig->get("ymode", ymode);
+    camconfig->get("target-x", target_x);
+    camconfig->get("target-y", target_y);
+    camconfig->get("max-speed-x", max_speed_x);
+    camconfig->get("max-speed-y", max_speed_y);
+    camconfig->get("dynamic-max-speed-x", dynamic_max_speed_x);
+    camconfig->get("dirchange-time", dirchange_time);
+    camconfig->get("clamp-x", clamp_x);
+    camconfig->get("clamp-y", clamp_y);
+    camconfig->get("kirby-rectsize-x", kirby_rectsize_x);
+    camconfig->get("kirby-rectsize-y", kirby_rectsize_y);
+    camconfig->get("edge-x", edge_x);
+    camconfig->get("sensitive-x", sensitive_x);
+    camconfig->get("dynamic-speed-sm", dynamic_speed_sm);
+  }
+};
+
+Camera::Camera(Sector* newsector, std::string name)
+  : mode(NORMAL), sector(newsector), lookahead_mode(LOOKAHEAD_NONE),
+    lookahead_pos(0)
+{
+  this->name = name;
+  config = new CameraConfig();
+  reload_config();
+}
+
+Camera::~Camera()
+{
+  delete config;
+}
+
+void
+Camera::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if(name.empty()) return;
+  Scripting::Camera* interface = new Scripting::Camera(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Camera::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if(name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Camera::draw(DrawingContext& )
+{
+}
+
+const Vector&
+Camera::get_translation() const
+{
+  return translation;
+}
+
+void
+Camera::parse(const lisp::Lisp& reader)
+{
+  std::string modename;
+
+  reader.get("mode", modename);
+  if(modename == "normal") {
+    mode = NORMAL;
+  } else if(modename == "autoscroll") {
+    mode = AUTOSCROLL;
+
+    const lisp::Lisp* pathLisp = reader.get_lisp("path");
+    if(pathLisp == NULL)
+      throw std::runtime_error("No path specified in autoscroll camera.");
+
+    autoscroll_path.reset(new Path());
+    autoscroll_path->read(*pathLisp);
+    autoscroll_walker.reset(new PathWalker(autoscroll_path.get()));
+  } else if(modename == "manual") {
+    mode = MANUAL;
+  } else {
+    std::stringstream str;
+    str << "invalid camera mode '" << modename << "'found in worldfile.";
+    throw std::runtime_error(str.str());
+  }
+}
+
+void
+Camera::write(lisp::Writer& writer)
+{
+  writer.start_list("camera");
+
+  if(mode == NORMAL) {
+    writer.write_string("mode", "normal");
+  } else if(mode == AUTOSCROLL) {
+    writer.write_string("mode", "autoscroll");
+    autoscroll_path->write(writer);
+  } else if(mode == MANUAL) {
+    writer.write_string("mode", "manual");
+  }
+
+  writer.end_list("camera");
+}
+
+void
+Camera::reset(const Vector& tuxpos)
+{
+  translation.x = tuxpos.x - SCREEN_WIDTH/3 * 2;
+  translation.y = tuxpos.y - SCREEN_HEIGHT/2;
+  shakespeed = 0;
+  shaketimer.stop();
+  keep_in_bounds(translation);
+}
+
+void
+Camera::shake(float time, float x, float y)
+{
+  shaketimer.start(time);
+  shakedepth_x = x;
+  shakedepth_y = y;
+  shakespeed = M_PI/2 / time;
+}
+
+void
+Camera::scroll_to(const Vector& goal, float scrolltime)
+{
+  scroll_from = translation;
+  scroll_goal = goal;
+  keep_in_bounds(scroll_goal);
+
+  scroll_to_pos = 0;
+  scrollspeed = 1.0 / scrolltime;
+  mode = SCROLLTO;
+}
+
+static const float EPSILON = .00001f;
+static const float MAX_SPEED_Y = 140;
+
+void
+Camera::update(float elapsed_time)
+{
+  switch(mode) {
+    case NORMAL:
+      update_scroll_normal(elapsed_time);
+      break;
+    case AUTOSCROLL:
+      update_scroll_autoscroll(elapsed_time);
+      break;
+    case SCROLLTO:
+      update_scroll_to(elapsed_time);
+      break;
+    default:
+      break;
+  }
+  shake();
+}
+
+void
+Camera::reload_config()
+{
+  config->load("camera.cfg");
+}
+
+float clamp(float val, float min, float max)
+{
+  if(val < min)
+    return min;
+  if(val > max)
+    return max;
+
+  return val;
+}
+
+void
+Camera::keep_in_bounds(Vector& translation)
+{
+  float width = sector->get_width();
+  float height = sector->get_height();
+
+  // don't scroll before the start or after the level's end
+  translation.x = clamp(translation.x, 0, width - SCREEN_WIDTH);
+  translation.y = clamp(translation.y, 0, height - SCREEN_HEIGHT);
+
+  if (height < SCREEN_HEIGHT)
+    translation.y = height/2.0 - SCREEN_HEIGHT/2.0;
+  if (width < SCREEN_WIDTH)
+    translation.x = width/2.0 - SCREEN_WIDTH/2.0;
+}
+
+void
+Camera::shake()
+{
+  if(shaketimer.started()) {
+    translation.x -= sin(shaketimer.get_timegone() * shakespeed) * shakedepth_x;
+    translation.y -= sin(shaketimer.get_timegone() * shakespeed) * shakedepth_y;
+  }
+}
+
+void
+Camera::update_scroll_normal(float elapsed_time)
+{
+  const CameraConfig& config = *(this->config);
+  Player* player = sector->player;
+  const Vector& player_pos = player->get_bbox().get_middle();
+  static Vector last_player_pos = player_pos;
+  Vector player_delta = player_pos - last_player_pos;
+  last_player_pos = player_pos;
+
+  // check that we don't have division by zero later
+  if(elapsed_time < EPSILON)
+    return;
+
+  /****** Vertical Scrolling part ******/
+  int xmode = config.xmode;
+  int ymode = config.ymode;
+
+  if(player->is_dying() || sector->get_height() == 19*32) {
+    ymode = 0;
+  }
+  if(player->is_dying())
+    xmode = 0;
+
+  if(ymode == 1) {
+    translation.y = player_pos.y - SCREEN_HEIGHT * config.target_y;
+  }
+  if(ymode == 2) {
+    // target_y is the high we target our scrolling at. This is not always the
+    // high of the player, but if he is jumping upwards we should use the
+    // position where he last touched the ground. (this probably needs
+    // exceptions for trampolines and similar things in the future)
+    float target_y;
+    if(player->fall_mode == Player::JUMPING)
+      target_y = player->last_ground_y + player->get_bbox().get_height();
+    else
+      target_y = player->get_bbox().p2.y;
+    target_y -= SCREEN_HEIGHT * config.target_y;
+
+    // delta_y is the distance we'd have to travel to directly reach target_y
+    float delta_y = translation.y - target_y;
+    // speed is the speed the camera would need to reach target_y in this frame
+    float speed_y = delta_y / elapsed_time;
+
+    // limit the camera speed when jumping upwards
+    if(player->fall_mode != Player::FALLING
+        && player->fall_mode != Player::TRAMPOLINE_JUMP) {
+      speed_y = clamp(speed_y, -config.max_speed_y, config.max_speed_y);
+    }
+
+    // scroll with calculated speed
+    translation.y -= speed_y * elapsed_time;
+  }
+  if(ymode == 3) {
+    float halfsize = config.kirby_rectsize_y * 0.5f;
+    translation.y = clamp(translation.y,
+        player_pos.y - SCREEN_HEIGHT * (0.5f + halfsize),
+        player_pos.y - SCREEN_HEIGHT * (0.5f - halfsize));
+  }
+  if(ymode == 4) {
+    // TODO...
+  }
+
+  if(ymode != 0 && config.clamp_y > 0) {
+    translation.y = clamp(translation.y,
+        player_pos.y - SCREEN_HEIGHT * (1-config.clamp_y),
+        player_pos.y - SCREEN_HEIGHT * config.clamp_y);
+  }
+
+  /****** Horizontal scrolling part *******/
+
+  if(xmode == 1) {
+    translation.x = player_pos.x - SCREEN_WIDTH * config.target_x;
+  }
+  if(xmode == 2) {
+    // our camera is either in leftscrolling, rightscrolling or
+    // nonscrollingmode.
+    //
+    // when suddenly changing directions while scrolling into the other
+    // direction abort scrolling, since tux might be going left/right at a
+    // relatively small part of the map (like when jumping upwards)
+
+    // Find out direction in which the player moves
+    LookaheadMode walkDirection;
+    if (player_delta.x < -EPSILON) walkDirection = LOOKAHEAD_LEFT;
+    else if (player_delta.x > EPSILON) walkDirection = LOOKAHEAD_RIGHT;
+    else if (player->dir == ::LEFT) walkDirection = LOOKAHEAD_LEFT;
+    else walkDirection = LOOKAHEAD_RIGHT;
+
+    float LEFTEND, RIGHTEND;
+    if(config.sensitive_x > 0) {
+      LEFTEND = SCREEN_WIDTH * config.sensitive_x;
+      RIGHTEND = SCREEN_WIDTH * (1-config.sensitive_x);
+    } else {
+      LEFTEND = SCREEN_WIDTH;
+      RIGHTEND = 0;
+    }
+
+    if(lookahead_mode == LOOKAHEAD_NONE) {
+      /* if we're undecided then look if we crossed the left or right
+       * "sensitive" area */
+      if(player_pos.x < translation.x + LEFTEND) {
+        lookahead_mode = LOOKAHEAD_LEFT;
+      } else if(player_pos.x > translation.x + RIGHTEND) {
+        lookahead_mode = LOOKAHEAD_RIGHT;
+      }
+      /* at the ends of a level it's obvious which way we will go */
+      if(player_pos.x < SCREEN_WIDTH*0.5) {
+        lookahead_mode = LOOKAHEAD_RIGHT;
+      } else if(player_pos.x >= sector->get_width() - SCREEN_WIDTH*0.5) {
+        lookahead_mode = LOOKAHEAD_LEFT;
+      }
+
+      changetime = -1;
+    } else if(lookahead_mode != walkDirection) {
+      /* player changed direction while camera was scrolling...
+       * he has to do this for a certain time to add robustness against
+       * sudden changes */
+      if(changetime < 0) {
+        changetime = game_time;
+      } else if(game_time - changetime > config.dirchange_time) {
+        if(lookahead_mode == LOOKAHEAD_LEFT &&
+           player_pos.x > translation.x + RIGHTEND) {
+          lookahead_mode = LOOKAHEAD_RIGHT;
+        } else if(lookahead_mode == LOOKAHEAD_RIGHT &&
+                  player_pos.x < translation.x + LEFTEND) {
+          lookahead_mode = LOOKAHEAD_LEFT;
+        } else {
+          lookahead_mode = LOOKAHEAD_NONE;
+        }
+      }
+    } else {
+      changetime = -1;
+    }
+
+    LEFTEND = SCREEN_WIDTH * config.edge_x;
+    RIGHTEND = SCREEN_WIDTH * (1-config.edge_x);
+
+    // calculate our scroll target depending on scroll mode
+    float target_x;
+    if(lookahead_mode == LOOKAHEAD_LEFT)
+      target_x = player_pos.x - RIGHTEND;
+    else if(lookahead_mode == LOOKAHEAD_RIGHT)
+      target_x = player_pos.x - LEFTEND;
+    else
+      target_x = translation.x;
+
+    // that's the distance we would have to travel to reach target_x
+    float delta_x = translation.x - target_x;
+    // the speed we'd need to travel to reach target_x in this frame
+    float speed_x = delta_x / elapsed_time;
+
+    // limit our speed
+    float player_speed_x = player_delta.x / elapsed_time;
+    float maxv = config.max_speed_x + (fabsf(player_speed_x * config.dynamic_max_speed_x));
+    speed_x = clamp(speed_x, -maxv, maxv);
+
+    // If player is peeking scroll in that direction. Fast.
+    if(player->peeking_direction() == ::LEFT) {
+      speed_x = config.max_speed_x;
+    }
+    if(player->peeking_direction() == ::RIGHT) {
+      speed_x = -config.max_speed_x;
+    }
+
+    // apply scrolling
+    translation.x -= speed_x * elapsed_time;
+  }
+  if(xmode == 3) {
+    float halfsize = config.kirby_rectsize_x * 0.5f;
+    translation.x = clamp(translation.x,
+        player_pos.x - SCREEN_WIDTH * (0.5f + halfsize),
+        player_pos.x - SCREEN_WIDTH * (0.5f - halfsize));
+  }
+  if(xmode == 4) {
+    float LEFTEND = SCREEN_WIDTH * config.edge_x;
+    float RIGHTEND = SCREEN_WIDTH * (1 - config.edge_x);
+
+    if (player_delta.x < -EPSILON) {
+      // walking left
+      lookahead_pos -= player_delta.x * config.dynamic_speed_sm;
+
+      if(lookahead_pos > RIGHTEND) {
+        lookahead_pos = RIGHTEND;
+      }
+    } else if (player_delta.x > EPSILON) {
+      // walking right
+      lookahead_pos -= player_delta.x * config.dynamic_speed_sm;
+      if(lookahead_pos < LEFTEND) {
+        lookahead_pos = LEFTEND;
+      }
+    }
+
+    if(player->peeking_direction() == ::LEFT) {
+      lookahead_pos += config.max_speed_x * elapsed_time * 3.0f;
+    } else if(player->peeking_direction() == ::RIGHT) {
+      lookahead_pos -= config.max_speed_x * elapsed_time * 3.0f;
+    }
+
+    // adjust for level ends
+    if (player_pos.x < LEFTEND) {
+      lookahead_pos = LEFTEND;
+    }
+    if (player_pos.x > sector->get_width() - LEFTEND) {
+      lookahead_pos = RIGHTEND;
+    }
+
+    translation.x = player_pos.x - lookahead_pos;
+  }
+
+  if(xmode != 0 && config.clamp_x > 0) {
+    translation.x = clamp(translation.x,
+        player_pos.x - SCREEN_WIDTH * (1-config.clamp_x),
+        player_pos.x - SCREEN_WIDTH * config.clamp_x);
+  }
+
+  keep_in_bounds(translation);
+}
+
+void
+Camera::update_scroll_autoscroll(float elapsed_time)
+{
+  Player* player = sector->player;
+  if(player->is_dying())
+    return;
+
+  translation = autoscroll_walker->advance(elapsed_time);
+
+  keep_in_bounds(translation);
+}
+
+void
+Camera::update_scroll_to(float elapsed_time)
+{
+  scroll_to_pos += elapsed_time * scrollspeed;
+  if(scroll_to_pos >= 1.0) {
+    mode = MANUAL;
+    translation = scroll_goal;
+    return;
+  }
+
+  translation = scroll_from + (scroll_goal - scroll_from) * scroll_to_pos;
+}
diff --git a/src/object/camera.hpp b/src/object/camera.hpp
new file mode 100644 (file)
index 0000000..d5ca2d0
--- /dev/null
@@ -0,0 +1,132 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_CAMERA_H
+#define SUPERTUX_CAMERA_H
+
+#include <vector>
+#include <cassert>
+#include <memory>
+
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "video/drawing_context.hpp"
+#include "serializable.hpp"
+#include "timer.hpp"
+#include "script_interface.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Sector;
+class Path;
+class PathWalker;
+class CameraConfig;
+
+class Camera : public GameObject, public Serializable, public ScriptInterface
+{
+public:
+  Camera(Sector* sector, std::string name = "");
+  virtual ~Camera();
+
+  /// parse camera mode from lisp file
+  void parse(const lisp::Lisp& reader);
+  /// write camera mode to a lisp file
+  virtual void write(lisp::Writer& writer);
+
+  /// reset camera postion
+  void reset(const Vector& tuxpos);
+
+  /** return camera position */
+  const Vector& get_translation() const;
+
+  virtual void update(float elapsed_time);
+
+  virtual void draw(DrawingContext& );
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  // shake camera in a direction 1 time
+  void shake(float speed, float x, float y);
+
+  void set_scrolling(int scroll_x, int scroll_y)
+  {
+    translation.x = scroll_x;
+    translation.y = scroll_y;
+  }
+
+  /**
+   * scroll the upper left edge of the camera in scrolltime seconds
+   * to the position goal
+   */
+  void scroll_to(const Vector& goal, float scrolltime);
+
+  void reload_config();
+
+  enum CameraMode
+  {
+    NORMAL, AUTOSCROLL, SCROLLTO, MANUAL
+  };
+  CameraMode mode;
+
+private:
+  void update_scroll_normal(float elapsed_time);
+  void update_scroll_autoscroll(float elapsed_time);
+  void update_scroll_to(float elapsed_time);
+  void keep_in_bounds(Vector& vector);
+  void shake();
+
+  /**
+   * The camera basically provides lookeahead on the left or right side
+   * or is undecided.
+   */
+  enum LookaheadMode {
+    LOOKAHEAD_NONE, LOOKAHEAD_LEFT, LOOKAHEAD_RIGHT
+  };
+
+  Vector translation;
+
+  Sector* sector;
+
+  // normal mode
+  LookaheadMode lookahead_mode;
+  float changetime;
+  float lookahead_pos;
+
+  // autoscroll mode
+  std::auto_ptr<Path> autoscroll_path;
+  std::auto_ptr<PathWalker> autoscroll_walker;
+
+  // shaking
+  Timer shaketimer;
+  float shakespeed;
+  float shakedepth_x;
+  float shakedepth_y;
+
+  // scrollto mode
+  Vector scroll_from;
+  Vector scroll_goal;
+  float scroll_to_pos;
+  float scrollspeed;
+
+  CameraConfig *config;
+};
+
+#endif /*SUPERTUX_CAMERA_H*/
diff --git a/src/object/candle.cpp b/src/object/candle.cpp
new file mode 100644 (file)
index 0000000..98c48c1
--- /dev/null
@@ -0,0 +1,117 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "candle.hpp"
+#include "scripting/candle.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "sector.hpp"
+#include "object/sprite_particle.hpp"
+#include "object_factory.hpp"
+#include "random_generator.hpp"
+
+Candle::Candle(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/candle/candle.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_DISABLED), burning(true),
+       candle_light_1("images/objects/candle/candle-light-1.png"),
+       candle_light_2("images/objects/candle/candle-light-2.png")
+{
+  lisp.get("name", name);
+  lisp.get("burning", burning);
+
+  if (burning) {
+    sprite->set_action("on");
+  } else {
+    sprite->set_action("off");
+  }
+
+}
+
+void
+Candle::draw(DrawingContext& context)
+{
+  // draw regular sprite
+  sprite->draw(context, get_pos(), layer);
+
+  // draw on lightmap
+  if (burning) {
+    Vector pos = get_pos() + (bbox.get_size() - candle_light_1.get_size()) / 2;
+    context.push_target();
+    context.set_target(DrawingContext::LIGHTMAP);
+    // draw approx. 1 in 10 frames darker. Makes the candle flicker
+    if (systemRandom.rand(10) != 0) {
+      context.draw_surface(&candle_light_1, pos, layer);
+    } else {
+      context.draw_surface(&candle_light_2, pos, layer);
+    }
+    context.pop_target();
+  }
+}
+
+HitResponse
+Candle::collision(GameObject&, const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
+
+void
+Candle::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::Candle* interface = new Scripting::Candle(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Candle::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Candle::puff_smoke()
+{
+  Vector ppos = bbox.get_middle();
+  Vector pspeed = Vector(0, -150);
+  Vector paccel = Vector(0,0);
+  Sector::current()->add_object(new SpriteParticle("images/objects/particles/smoke.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_BACKGROUNDTILES+2));
+}
+
+bool
+Candle::get_burning()
+{
+  return burning;
+}
+
+void
+Candle::set_burning(bool burning)
+{
+  if (this->burning == burning) return;
+  this->burning = burning;
+  if (burning) {
+    sprite->set_action("on");
+    puff_smoke();
+  } else {
+    sprite->set_action("off");
+    puff_smoke();
+  }
+}
+
+IMPLEMENT_FACTORY(Candle, "candle");
diff --git a/src/object/candle.hpp b/src/object/candle.hpp
new file mode 100644 (file)
index 0000000..c82db5e
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __CANDLE_H__
+#define __CANDLE_H__
+
+#include <string>
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "script_interface.hpp"
+#include "video/surface.hpp"
+
+/**
+ * A burning candle: Simple, scriptable level decoration.
+ */
+class Candle : public MovingSprite, public ScriptInterface
+{
+public:
+  Candle(const lisp::Lisp& lisp);
+  virtual Candle* clone() const { return new Candle(*this); }
+  virtual void draw(DrawingContext& context);
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  /**
+   * @name Scriptable Methods
+   * @{
+   */
+  void puff_smoke(); /**< spawn a puff of smoke */
+  bool get_burning(); /**< returns true if candle is lighted */
+  void set_burning(bool burning); /**< true: light candle, false: extinguish candle */
+  /**
+   * @}
+   */
+
+private:
+  bool burning; /**< true if candle is currently lighted */
+  Surface candle_light_1; /**< drawn to lightmap */
+  Surface candle_light_2; /**< drawn to lightmap (alternative image) */
+
+};
+
+#endif
diff --git a/src/object/coin.cpp b/src/object/coin.cpp
new file mode 100644 (file)
index 0000000..c75312f
--- /dev/null
@@ -0,0 +1,140 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "coin.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "player_status.hpp"
+#include "gameobjs.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+#include "random_generator.hpp"
+#include "audio/sound_source.hpp"
+#include "audio/sound_manager.hpp"
+#include "timer.hpp"
+
+Coin::Coin(const Vector& pos)
+       : MovingSprite(pos, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
+{
+  sound_manager->preload("sounds/coin.wav");
+}
+
+Coin::Coin(const lisp::Lisp& reader)
+       : MovingSprite(reader, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
+{
+  sound_manager->preload("sounds/coin.wav");
+}
+
+void
+Coin::collect()
+{
+  // TODO: commented out musical code. Maybe fork this for a special "MusicalCoin" object?
+  /*
+  static Timer sound_timer;
+  static int pitch_one = 128;
+  static float last_pitch = 1;
+  float pitch = 1;
+
+  int tile = static_cast<int>(get_pos().y / 32);
+
+  if (!sound_timer.started()) {
+    pitch_one = tile;
+    pitch = 1;
+    last_pitch = 1;
+  }
+  else if (sound_timer.get_timegone() < 0.02) {
+    pitch = last_pitch;
+  }
+  else
+  {
+    switch ((pitch_one - tile) % 7) {
+      case -6:
+       pitch = 1.0/2;
+       break;
+      case -5:
+       pitch = 5.0/8;
+       break;
+      case -4:
+       pitch = 4.0/6;
+       break;
+      case -3:
+       pitch = 3.0/4;
+       break;
+      case -2:
+       pitch = 5.0/6;
+       break;
+      case -1:
+       pitch = 9.0/10;
+       break;
+      case 0:
+       pitch = 1.0;
+       break;
+      case 1:
+       pitch = 9.0/8;
+       break;
+      case 2:
+       pitch = 5.0/4;
+       break;
+      case 3:
+       pitch = 4.0/3;
+       break;
+      case 4:
+       pitch = 3.0/2;
+       break;
+      case 5:
+       pitch = 5.0/3;
+       break;
+      case 6:
+       pitch = 9.0/5;
+       break;
+    }
+    last_pitch = pitch;
+  }
+  sound_timer.start(1);
+
+  SoundSource* soundSource = sound_manager->create_sound_source("sounds/coin.wav");
+  soundSource->set_position(get_pos());
+  soundSource->set_pitch(pitch);
+  soundSource->play();
+  sound_manager->manage_source(soundSource);
+*/
+  Sector::current()->player->get_status()->add_coins(1);
+  Sector::current()->add_object(new BouncyCoin(get_pos()));
+  Sector::current()->get_level()->stats.coins++;
+  remove_me();
+}
+
+HitResponse
+Coin::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if(player == 0)
+    return ABORT_MOVE;
+
+  collect();
+  return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Coin, "coin");
diff --git a/src/object/coin.hpp b/src/object/coin.hpp
new file mode 100644 (file)
index 0000000..16d04c9
--- /dev/null
@@ -0,0 +1,37 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __COIN_H__
+#define __COIN_H__
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+class Coin : public MovingSprite
+{
+public:
+  Coin(const Vector& pos);
+  Coin(const lisp::Lisp& reader);
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  void collect();
+};
+
+#endif
diff --git a/src/object/display_effect.cpp b/src/object/display_effect.cpp
new file mode 100644 (file)
index 0000000..acf52bf
--- /dev/null
@@ -0,0 +1,196 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "display_effect.hpp"
+
+#include <assert.h>
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+
+static const float BORDER_SIZE = 75;
+
+DisplayEffect::DisplayEffect(std::string name)
+  : screen_fade(NO_FADE), screen_fadetime(0), screen_fading(0),
+    border_fade(NO_FADE), border_fadetime(0), border_size(0), black(false),
+    borders(false)
+{
+  this->name = name;
+}
+
+DisplayEffect::~DisplayEffect()
+{
+}
+
+void
+DisplayEffect::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  expose_object(vm, table_idx, dynamic_cast<Scripting::DisplayEffect *>(this), name, false);
+}
+
+void
+DisplayEffect::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+DisplayEffect::update(float elapsed_time)
+{
+  switch(screen_fade) {
+  case NO_FADE:
+    break;
+  case FADE_IN:
+    screen_fading -= elapsed_time;
+    if(screen_fading < 0) {
+      screen_fade = NO_FADE;
+    }
+    break;
+  case FADE_OUT:
+    screen_fading -= elapsed_time;
+    if(screen_fading < 0) {
+      screen_fade = NO_FADE;
+      black = true;
+    }
+    break;
+  default:
+    assert(false);
+  }
+
+  switch(border_fade) {
+  case NO_FADE:
+    break;
+  case FADE_IN:
+    border_fading -= elapsed_time;
+    if(border_fading < 0) {
+      border_fade = NO_FADE;
+    }
+    border_size = (border_fadetime - border_fading)
+      / border_fadetime * BORDER_SIZE;
+    break;
+  case FADE_OUT:
+    border_fading -= elapsed_time;
+    if(border_fading < 0) {
+      borders = false;
+      border_fade = NO_FADE;
+    }
+    border_size = border_fading / border_fadetime * BORDER_SIZE;
+    break;
+  default:
+    assert(false);
+  }
+}
+
+void
+DisplayEffect::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  if(black || screen_fade != NO_FADE) {
+    float alpha;
+    if(black) {
+      alpha = 1.0f;
+    } else {
+      switch(screen_fade) {
+      case FADE_IN:
+        alpha = screen_fading / screen_fadetime;
+        break;
+      case FADE_OUT:
+        alpha = (screen_fadetime - screen_fading) / screen_fadetime;
+        break;
+      default:
+        alpha = 0;
+        assert(false);
+      }
+    }
+    context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+        Color(0, 0, 0, alpha), LAYER_GUI-10);
+  }
+
+  if (borders) {
+    context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, border_size),
+        Color(0, 0, 0, 1.0f), LAYER_GUI-10);
+    context.draw_filled_rect(Vector(0, SCREEN_HEIGHT - border_size), Vector(SCREEN_WIDTH, border_size),
+        Color(0, 0, 0, 1.0f), LAYER_GUI-10);
+  }
+
+  context.pop_transform();
+}
+
+void
+DisplayEffect::fade_out(float fadetime)
+{
+  black = false;
+  screen_fadetime = fadetime;
+  screen_fading = fadetime;
+  screen_fade = FADE_OUT;
+}
+
+void
+DisplayEffect::fade_in(float fadetime)
+{
+  black = false;
+  this->screen_fadetime = fadetime;
+  screen_fading = fadetime;
+  screen_fade = FADE_IN;
+}
+
+void
+DisplayEffect::set_black(bool enabled)
+{
+  black = enabled;
+}
+
+bool
+DisplayEffect::is_black()
+{
+  return black;
+}
+
+void
+DisplayEffect::sixteen_to_nine(float fadetime)
+{
+  if(fadetime == 0) {
+    borders = true;
+    border_size = BORDER_SIZE;
+  } else {
+    borders = true;
+    border_size = 0;
+    border_fade = FADE_IN;
+    border_fadetime = fadetime;
+    border_fading = border_fadetime;
+  }
+}
+
+void
+DisplayEffect::four_to_three(float fadetime)
+{
+  if(fadetime == 0) {
+    borders = false;
+  } else {
+    border_size = BORDER_SIZE;
+    border_fade = FADE_OUT;
+    border_fadetime = fadetime;
+    border_fading = border_fadetime;
+  }
+}
diff --git a/src/object/display_effect.hpp b/src/object/display_effect.hpp
new file mode 100644 (file)
index 0000000..8c3f857
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __OBJECT_DISPLAY_EFFECT_H__
+#define __OBJECT_DISPLAY_EFFECT_H__
+
+#include "scripting/display_effect.hpp"
+#include "game_object.hpp"
+#include "script_interface.hpp"
+
+class DisplayEffect : public GameObject, public Scripting::DisplayEffect,
+                      public ScriptInterface
+{
+public:
+    DisplayEffect(std::string name = "");
+    virtual ~DisplayEffect();
+
+    void expose(HSQUIRRELVM vm, SQInteger table_idx);
+    void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+    void update(float elapsed_time);
+    void draw(DrawingContext& context);
+
+
+    /**
+     * @name Scriptable Methods
+     * @{
+     */
+
+    void fade_out(float fadetime);
+    void fade_in(float fadetime);
+    void set_black(bool enabled);
+    bool is_black();
+    void sixteen_to_nine(float fadetime);
+    void four_to_three(float fadetime);
+
+    /**
+     * @}
+     */
+
+private:
+    enum FadeType {
+        NO_FADE, FADE_IN, FADE_OUT
+    };
+    FadeType screen_fade;
+    float screen_fadetime;
+    float screen_fading;
+    FadeType border_fade;
+    float border_fadetime;
+    float border_fading;
+    float border_size;
+
+    bool black;
+    bool borders;
+};
+
+#endif
diff --git a/src/object/electrifier.cpp b/src/object/electrifier.cpp
new file mode 100644 (file)
index 0000000..f1b8c1c
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "electrifier.hpp"
+#include "sector.hpp"
+#include "object/tilemap.hpp"
+#include "tile.hpp"
+
+
+Electrifier::Electrifier(uint32_t oldtile, uint32_t newtile, float seconds)
+{
+  duration.start(seconds);
+  change_from = oldtile;
+  change_to = newtile;
+  Sector::current()->change_solid_tiles(change_from,change_to);
+}
+
+Electrifier::~Electrifier() {
+}
+
+void
+Electrifier::update(float )
+{
+  if (duration.check()) {
+    Sector::current()->change_solid_tiles(change_to,change_from);
+    remove_me();
+  }
+}
+
+void
+Electrifier::draw(DrawingContext& )
+{
+}
diff --git a/src/object/electrifier.hpp b/src/object/electrifier.hpp
new file mode 100644 (file)
index 0000000..3ad746c
--- /dev/null
@@ -0,0 +1,44 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ELECTRIFIER_H__
+#define __ELECTRIFIER_H__
+
+#include "resources.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+#include <stdint.h>
+
+//Changes all tiles with the given ID to a new one for a given amount of time, then removes itself
+//Used by the Kugelblitz to electrify water - can be used for other effects, too
+class Electrifier : public GameObject
+{
+public:
+  Electrifier(uint32_t oldtile, uint32_t newtile, float seconds);
+  ~Electrifier();
+protected:
+  virtual void update(float time);
+  virtual void draw(DrawingContext& context);
+private:
+  uint32_t change_from;
+  uint32_t change_to;
+  Timer duration;
+};
+
+#endif
diff --git a/src/object/endsequence.cpp b/src/object/endsequence.cpp
new file mode 100644 (file)
index 0000000..2e4cc6a
--- /dev/null
@@ -0,0 +1,117 @@
+//  $Id$
+//
+//  SuperTux - End Sequence
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "endsequence.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+#include "scripting/level_time.hpp"
+#include "scripting/squirrel_util.hpp"
+
+EndSequence::EndSequence()
+: isrunning(false), isdone(false), tux_may_walk(true)
+{
+  end_sequence_controller = 0;
+}
+
+EndSequence::~EndSequence()
+{
+  delete end_sequence_controller;
+}
+
+void
+EndSequence::update(float elapsed_time)
+{
+  if (!isrunning) return;
+  running(elapsed_time);
+}
+
+void
+EndSequence::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequence::start()
+{
+  if (isrunning) return;
+  isrunning = true;
+  isdone = false;
+
+  Player& tux = *Sector::current()->player;
+  end_sequence_controller = new CodeController();
+  tux.set_controller(end_sequence_controller);
+  tux.set_speedlimit(230); //MAX_WALK_XM
+
+  starting();
+}
+
+void
+EndSequence::stop_tux()
+{
+  tux_may_walk = false;
+}
+
+void
+EndSequence::stop()
+{
+  if (!isrunning) return;
+  isrunning = false;
+  isdone = true;
+  stopping();
+}
+
+bool
+EndSequence::is_tux_stopped()
+{
+  return !tux_may_walk;
+}
+
+ bool
+EndSequence::is_done()
+{
+  return isdone;
+}
+
+void
+EndSequence::starting()
+{
+}
+
+void
+EndSequence::running(float /*elapsed_time*/)
+{
+}
+
+void
+EndSequence::stopping()
+{
+}
diff --git a/src/object/endsequence.hpp b/src/object/endsequence.hpp
new file mode 100644 (file)
index 0000000..f7ac647
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux - End Sequence
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ENDSEQUENCE_H__
+#define __ENDSEQUENCE_H__
+
+#include <memory>
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "control/codecontroller.hpp"
+
+class EndSequence : public GameObject
+{
+public:
+    EndSequence();
+    virtual ~EndSequence();
+
+    virtual void update(float elapsed_time);
+    virtual void draw(DrawingContext& context);
+
+    void start(); /**< play EndSequence */
+    void stop_tux(); /**< called when Tux has reached his final position */
+    void stop(); /**< stop playing EndSequence, mark it as done playing */
+    bool is_tux_stopped(); /**< returns true if Tux has reached his final position */
+    bool is_done(); /**< returns true if EndSequence has finished playing */
+
+protected:
+    virtual void starting(); /**< called when EndSequence starts */
+    virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+    virtual void stopping(); /**< called when EndSequence stops */
+
+    bool isrunning; /**< true while EndSequence plays */
+    bool isdone; /**< true if EndSequence has finished playing */
+    bool tux_may_walk; /**< true while tux is allowed to walk */
+    CodeController* end_sequence_controller;
+
+};
+
+#endif
diff --git a/src/object/endsequence_fireworks.cpp b/src/object/endsequence_fireworks.cpp
new file mode 100644 (file)
index 0000000..7cd940f
--- /dev/null
@@ -0,0 +1,66 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "endsequence_fireworks.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+#include "object/fireworks.hpp"
+
+EndSequenceFireworks::EndSequenceFireworks()
+: EndSequence()
+{
+}
+
+EndSequenceFireworks::~EndSequenceFireworks()
+{
+}
+
+void
+EndSequenceFireworks::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceFireworks::starting()
+{
+  EndSequence::starting();
+  endsequence_timer.start(7.3f * main_loop->get_speed());
+  Sector::current()->add_object(new Fireworks());
+}
+
+void
+EndSequenceFireworks::running(float elapsed_time)
+{
+  EndSequence::running(elapsed_time);
+  //Player& tux = *Sector::current()->player;
+
+  if (tux_may_walk) {
+    end_sequence_controller->press(Controller::JUMP);
+  }
+
+  if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceFireworks::stopping()
+{
+  EndSequence::stopping();
+}
diff --git a/src/object/endsequence_fireworks.hpp b/src/object/endsequence_fireworks.hpp
new file mode 100644 (file)
index 0000000..aaa7b79
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ENDSEQUENCE_FIREWORKS_H__
+#define __ENDSEQUENCE_FIREWORKS_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceFireworks : public EndSequence
+{
+public:
+    EndSequenceFireworks();
+    virtual ~EndSequenceFireworks();
+    virtual void draw(DrawingContext& context);
+
+protected:
+    virtual void starting(); /**< called when EndSequence starts */
+    virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+    virtual void stopping(); /**< called when EndSequence stops */
+
+    Timer endsequence_timer;
+};
+
+#endif
diff --git a/src/object/endsequence_walkleft.cpp b/src/object/endsequence_walkleft.cpp
new file mode 100644 (file)
index 0000000..bf87105
--- /dev/null
@@ -0,0 +1,70 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "endsequence_walkleft.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+
+EndSequenceWalkLeft::EndSequenceWalkLeft()
+: EndSequence()
+{
+}
+
+EndSequenceWalkLeft::~EndSequenceWalkLeft()
+{
+}
+
+void
+EndSequenceWalkLeft::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceWalkLeft::starting()
+{
+  EndSequence::starting();
+  last_x_pos = -1;
+  endsequence_timer.start(7.3f * main_loop->get_speed());
+}
+
+void
+EndSequenceWalkLeft::running(float elapsed_time)
+{
+  EndSequence::running(elapsed_time);
+  Player& tux = *Sector::current()->player;
+
+  if (tux_may_walk) {
+    end_sequence_controller->press(Controller::LEFT);
+    if (int(last_x_pos) == int(tux.get_pos().x)) {
+      end_sequence_controller->press(Controller::JUMP);
+    }
+  }
+
+  last_x_pos = tux.get_pos().x;
+
+  if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceWalkLeft::stopping()
+{
+  EndSequence::stopping();
+}
diff --git a/src/object/endsequence_walkleft.hpp b/src/object/endsequence_walkleft.hpp
new file mode 100644 (file)
index 0000000..6abac4d
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ENDSEQUENCE_WALKLEFT_H__
+#define __ENDSEQUENCE_WALKLEFT_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceWalkLeft : public EndSequence
+{
+public:
+    EndSequenceWalkLeft();
+    virtual ~EndSequenceWalkLeft();
+    virtual void draw(DrawingContext& context);
+
+protected:
+    virtual void starting(); /**< called when EndSequence starts */
+    virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+    virtual void stopping(); /**< called when EndSequence stops */
+
+    float last_x_pos;
+    Timer endsequence_timer;
+};
+
+#endif
diff --git a/src/object/endsequence_walkright.cpp b/src/object/endsequence_walkright.cpp
new file mode 100644 (file)
index 0000000..32f529e
--- /dev/null
@@ -0,0 +1,70 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include "endsequence_walkright.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+
+EndSequenceWalkRight::EndSequenceWalkRight()
+: EndSequence()
+{
+}
+
+EndSequenceWalkRight::~EndSequenceWalkRight()
+{
+}
+
+void
+EndSequenceWalkRight::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceWalkRight::starting()
+{
+  EndSequence::starting();
+  last_x_pos = -1;
+  endsequence_timer.start(7.3f * main_loop->get_speed());
+}
+
+void
+EndSequenceWalkRight::running(float elapsed_time)
+{
+  EndSequence::running(elapsed_time);
+  Player& tux = *Sector::current()->player;
+
+  if (tux_may_walk) {
+    end_sequence_controller->press(Controller::RIGHT);
+    if (int(last_x_pos) == int(tux.get_pos().x)) {
+      end_sequence_controller->press(Controller::JUMP);
+    }
+  }
+
+  last_x_pos = tux.get_pos().x;
+
+  if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceWalkRight::stopping()
+{
+  EndSequence::stopping();
+}
diff --git a/src/object/endsequence_walkright.hpp b/src/object/endsequence_walkright.hpp
new file mode 100644 (file)
index 0000000..aae4a2c
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux - End Sequence: Tux walks right
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ENDSEQUENCE_WALKRIGHT_H__
+#define __ENDSEQUENCE_WALKRIGHT_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceWalkRight : public EndSequence
+{
+public:
+    EndSequenceWalkRight();
+    virtual ~EndSequenceWalkRight();
+    virtual void draw(DrawingContext& context);
+
+protected:
+    virtual void starting(); /**< called when EndSequence starts */
+    virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+    virtual void stopping(); /**< called when EndSequence stops */
+
+    float last_x_pos;
+    Timer endsequence_timer;
+};
+
+#endif
diff --git a/src/object/explosion.cpp b/src/object/explosion.cpp
new file mode 100644 (file)
index 0000000..c172e11
--- /dev/null
@@ -0,0 +1,98 @@
+//  $Id$
+//
+//  SuperTux -- Explosion object
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "explosion.hpp"
+#include "badguy/badguy.hpp"
+#include "object/sprite_particle.hpp"
+#include "random_generator.hpp"
+
+Explosion::Explosion(const Vector& pos)
+       : MovingSprite(pos, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_TOUCHABLE), state(STATE_WAITING)
+{
+  sound_manager->preload("sounds/explosion.wav");
+  set_pos(get_pos() - (get_bbox().get_middle() - get_pos()));
+}
+
+Explosion::Explosion(const lisp::Lisp& reader)
+       : MovingSprite(reader, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_TOUCHABLE), state(STATE_WAITING)
+{
+  sound_manager->preload("sounds/explosion.wav");
+}
+
+void
+Explosion::explode()
+{
+  if (state != STATE_WAITING) return;
+  state = STATE_EXPLODING;
+
+  set_action("default", 1);
+  sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action
+  sound_manager->play("sounds/explosion.wav", get_pos());
+
+  // spawn some particles
+  // TODO: provide convenience function in MovingSprite or MovingObject?
+  for (int i = 0; i < 100; i++) {
+    Vector ppos = bbox.get_middle();
+    float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+    float velocity = systemRandom.randf(450, 900);
+    float vx = sin(angle)*velocity;
+    float vy = -cos(angle)*velocity;
+    Vector pspeed = Vector(vx, vy);
+    Vector paccel = Vector(0, 1000);
+    Sector::current()->add_object(new SpriteParticle("images/objects/particles/explosion.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+  }
+}
+
+void 
+Explosion::update(float )
+{
+  switch(state) {
+    case STATE_WAITING:
+      explode();
+      break;
+    case STATE_EXPLODING:
+      if(sprite->animation_done()) {
+       remove_me();
+      }
+      break;
+  }
+}
+
+HitResponse
+Explosion::collision(GameObject& other, const CollisionHit& )
+{
+  if(state != STATE_EXPLODING) return ABORT_MOVE;
+
+  Player* player = dynamic_cast<Player*>(&other);
+  if(player != 0) {
+    player->kill(false);
+  }
+
+  BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
+  if(badguy != 0) {
+    badguy->kill_fall();
+  }
+
+  return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Explosion, "explosion");
+
diff --git a/src/object/explosion.hpp b/src/object/explosion.hpp
new file mode 100644 (file)
index 0000000..4ae40e4
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux -- Explosion object
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __EXPLOSION_H__
+#define __EXPLOSION_H__
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+/**
+ * Just your average explosion - goes boom, hurts Tux
+ */
+class Explosion : public MovingSprite
+{
+public:
+  /**
+   * Create new Explosion centered(!) at @c pos
+   */
+  Explosion(const Vector& pos);
+  Explosion(const lisp::Lisp& reader);
+
+  void update(float elapsed_time);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+  /**
+   * plays sound, starts animation
+   */
+  void explode();
+
+private:
+  enum State {
+    STATE_WAITING,
+    STATE_EXPLODING
+  };
+  State state;
+
+};
+
+#endif
+
diff --git a/src/object/falling_coin.cpp b/src/object/falling_coin.cpp
new file mode 100644 (file)
index 0000000..8e091a5
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "falling_coin.hpp"
+#include "player.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+FallingCoin::FallingCoin(const Vector& start_position, const int vel_x)
+{
+  pos = start_position;
+  sprite = sprite_manager->create("images/objects/coin/coin.sprite");
+  physic.set_velocity_y(-800);
+  physic.set_velocity_x(vel_x);
+}
+
+FallingCoin::~FallingCoin()
+{
+  delete sprite;
+}
+
+void
+FallingCoin::draw(DrawingContext& context)
+{
+  sprite->draw(context, pos, LAYER_FLOATINGOBJECTS + 5);
+}
+
+void
+FallingCoin::update(float elapsed_time)
+{
+  pos += physic.get_movement(elapsed_time);
+  if (pos.y > SCREEN_HEIGHT)
+    remove_me();
+}
diff --git a/src/object/falling_coin.hpp b/src/object/falling_coin.hpp
new file mode 100644 (file)
index 0000000..8eeabb3
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FALLING_COIN_H__
+#define __FALLING_COIN_H__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "physic.hpp"
+
+class FallingCoin : public GameObject, private UsesPhysic
+{
+public:
+  FallingCoin(const Vector& start_position, const int x_vel);
+  ~FallingCoin();
+
+  void draw(DrawingContext& context);
+  void update(float elapsed_time);
+private:
+  Vector  pos;
+  Sprite* sprite;
+};
+
+#endif
diff --git a/src/object/firefly.cpp b/src/object/firefly.cpp
new file mode 100644 (file)
index 0000000..bd4f70a
--- /dev/null
@@ -0,0 +1,104 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "firefly.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player.hpp"
+#include "object_factory.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+Firefly::Firefly(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/resetpoints/default-resetpoint.sprite", LAYER_TILES, COLGROUP_TOUCHABLE), activated(false)
+{
+  initial_position = get_pos();
+  if( !lisp.get( "sprite", sprite_name ) ){
+    reactivate();
+    return;
+  }
+  if( sprite_name == "" ){
+    sprite_name = "images/objects/resetpoints/default-resetpoint.sprite";
+    reactivate();
+    return;
+  }
+  //Replace sprite
+  sprite = sprite_manager->create( sprite_name );
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  reactivate();
+}
+
+void
+Firefly::reactivate()
+{
+  if(GameSession::current()->get_reset_point_pos() == initial_position){
+    // TODO: && GameSession::current()->get_reset_point_sectorname() ==  <sector this firefly is in>
+    // GameSession::current()->get_current_sector()->get_name() is not yet initialized.
+    // Worst case a resetpoint in a different sector at the same position as the real
+    // resetpoint the player is spawning is set to ringing, too. Until we can check the sector, too, dont set
+    // activated = true; here.
+    sprite->set_action("ringing");
+  }
+}
+
+void
+Firefly::write(lisp::Writer& writer)
+{
+  writer.start_list("firefly");
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.end_list("firefly");
+}
+
+HitResponse
+Firefly::collision(GameObject& other, const CollisionHit& )
+{
+  if(activated)
+    return ABORT_MOVE;
+
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player) {
+    activated = true;
+// spawn some particles
+// TODO: provide convenience function in MovingSprite or MovingObject?
+          for (int i = 0; i < 5; i++) {
+            Vector ppos = bbox.get_middle();
+            float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+            float velocity = systemRandom.randf(450, 900);
+            float vx = sin(angle)*velocity;
+            float vy = -cos(angle)*velocity;
+            Vector pspeed = Vector(vx, vy);
+            Vector paccel = Vector(0, 1000);
+            Sector::current()->add_object(new SpriteParticle("images/objects/particles/reset.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+          }
+    // TODO play sound
+    sprite->set_action("ringing");
+    GameSession::current()->set_reset_point(Sector::current()->get_name(),
+        initial_position);
+  }
+
+  return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Firefly, "firefly");
diff --git a/src/object/firefly.hpp b/src/object/firefly.hpp
new file mode 100644 (file)
index 0000000..6325087
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FIREFLY_H__
+#define __FIREFLY_H__
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "serializable.hpp"
+#include "badguy/badguy.hpp"
+
+/**
+ * A Firefly: When tux touches it, it begins buzzing and you will respawn at this
+ * position.
+ */
+class Firefly : public MovingSprite, public Serializable
+{
+public:
+  Firefly(const lisp::Lisp& lisp);
+  virtual Firefly* clone() const { return new Firefly(*this); }
+
+  void write(lisp::Writer& writer);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+  bool activated;
+  Vector initial_position; /**< position as in level file. This is where Tux will have to respawn, as the level is reset every time */
+  void reactivate();
+};
+
+#endif
diff --git a/src/object/fireworks.cpp b/src/object/fireworks.cpp
new file mode 100644 (file)
index 0000000..d3037ea
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "fireworks.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "particles.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "random_generator.hpp"
+
+Fireworks::Fireworks()
+{
+  timer.start(.2f);
+  sound_manager->preload("sounds/fireworks.wav");
+}
+
+Fireworks::~Fireworks()
+{
+}
+
+void
+Fireworks::update(float )
+{
+    if(timer.check()) {
+        Sector* sector = Sector::current();
+        Vector pos = sector->camera->get_translation();
+        pos += Vector(systemRandom.randf(SCREEN_WIDTH),
+                      systemRandom.randf(SCREEN_HEIGHT/2));
+
+        float red = systemRandom.randf(1.0);
+        float green = systemRandom.randf(1.0);
+        //float red = 0.7;
+        //float green = 0.9;
+        (void) red;
+        (void) green;
+        sector->add_object(new Particles(pos, 0, 360, Vector(140, 140),
+                Vector(0, 0), 45, Color(red, green, 0), 3, 1.3f,
+                LAYER_FOREGROUND1+1));
+        sound_manager->play("sounds/fireworks.wav");
+        timer.start(systemRandom.randf(1.0, 1.5));
+    }
+}
+
+void
+Fireworks::draw(DrawingContext& )
+{
+}
diff --git a/src/object/fireworks.hpp b/src/object/fireworks.hpp
new file mode 100644 (file)
index 0000000..4128c83
--- /dev/null
@@ -0,0 +1,40 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FIREWORKS_H__
+#define __FIREWORKS_H__
+
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+
+class Fireworks : public GameObject
+{
+public:
+  Fireworks();
+  ~Fireworks();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Timer timer;
+};
+
+#endif
diff --git a/src/object/floating_image.cpp b/src/object/floating_image.cpp
new file mode 100644 (file)
index 0000000..00782af
--- /dev/null
@@ -0,0 +1,111 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include "resources.hpp"
+#include "main.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "floating_image.hpp"
+
+
+FloatingImage::FloatingImage(const std::string& spritefile)
+  : layer(LAYER_FOREGROUND1 + 1), visible(false), anchor(ANCHOR_MIDDLE), fading(0), fadetime(0)
+{
+  sprite.reset(sprite_manager->create(spritefile));
+}
+
+FloatingImage::~FloatingImage()
+{
+}
+
+void
+FloatingImage::update(float elapsed_time)
+{
+  if(fading > 0) {
+    fading -= elapsed_time;
+    if(fading <= 0) {
+      fading = 0;
+      visible = true;
+    }
+  } else if(fading < 0) {
+    fading += elapsed_time;
+    if(fading >= 0) {
+      fading = 0;
+      visible = false;
+    }
+  }
+
+//  (void) elapsed_time;
+}
+
+void
+FloatingImage::set_action(const std::string& action)
+{
+  sprite->set_action(action);
+}
+
+std::string
+FloatingImage::get_action()
+{
+  return sprite->get_action();
+}
+
+void
+FloatingImage::fade_in(float fadetime)
+{
+  this->fadetime = fadetime;
+  fading = fadetime;
+}
+
+void
+FloatingImage::fade_out(float fadetime)
+{
+  this->fadetime = fadetime;
+  fading = -fadetime;
+}
+
+
+void
+FloatingImage::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  if(fading > 0) {
+    context.set_alpha((fadetime-fading) / fadetime);
+  } else if(fading < 0) {
+    context.set_alpha(-fading / fadetime);
+  } else if(!visible) {
+    context.pop_transform();
+    return;
+  }
+
+  Vector spos = pos + get_anchor_pos(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+      sprite->get_width(), sprite->get_height(), anchor);
+
+  sprite->draw(context, spos, layer);
+
+  context.pop_transform();
+}
diff --git a/src/object/floating_image.hpp b/src/object/floating_image.hpp
new file mode 100644 (file)
index 0000000..19797db
--- /dev/null
@@ -0,0 +1,84 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FLOATING_IMAGE_H__
+#define __FLOATING_IMAGE_H__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "anchor_point.hpp"
+#include <memory>
+
+class Sprite;
+
+class FloatingImage : public GameObject
+{
+public:
+  FloatingImage(const std::string& sprite);
+  virtual ~FloatingImage();
+
+  void set_layer(int layer) {
+    this->layer = layer;
+  }
+
+  int get_layer() const {
+    return layer;
+  }
+
+  void set_pos(const Vector& pos) {
+    this->pos = pos;
+  }
+  const Vector& get_pos() const {
+    return pos;
+  }
+
+  void set_anchor_point(AnchorPoint anchor) {
+    this->anchor = anchor;
+  }
+  AnchorPoint get_anchor_point() const {
+    return anchor;
+  }
+
+  void set_visible(bool visible) {
+    this->visible = visible;
+  }
+  bool get_visible() const {
+    return visible;
+  }
+
+  void set_action(const std::string& action);
+  std::string get_action();
+
+  void fade_in(float fadetime);
+  void fade_out(float fadetime);
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+private:
+  std::auto_ptr<Sprite> sprite;
+  int layer;
+  bool visible;
+  AnchorPoint anchor;
+  Vector pos;
+  float fading;
+  float fadetime;
+};
+
+#endif
diff --git a/src/object/flower.cpp b/src/object/flower.cpp
new file mode 100644 (file)
index 0000000..71cacf2
--- /dev/null
@@ -0,0 +1,79 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include "flower.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+
+Flower::Flower(BonusType _type)
+  : type(_type)
+{
+  bbox.set_size(32, 32);
+
+  if(type == FIRE_BONUS) {
+    sprite = sprite_manager->create("images/powerups/fireflower/fireflower.sprite");
+    sound_manager->preload("sounds/fire-flower.wav");
+  }
+  else if(type == ICE_BONUS) {
+    sprite = sprite_manager->create("images/powerups/iceflower/iceflower.sprite");
+  } else {
+    assert(false);
+  }
+
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+Flower::~Flower()
+{
+  delete sprite;
+}
+
+void
+Flower::update(float )
+{
+}
+
+void
+Flower::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+HitResponse
+Flower::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if(!player)
+    return ABORT_MOVE;
+
+  if(!player->add_bonus(type, true))
+    return FORCE_MOVE;
+
+  sound_manager->play("sounds/fire-flower.wav");
+  remove_me();
+  return ABORT_MOVE;
+}
diff --git a/src/object/flower.hpp b/src/object/flower.hpp
new file mode 100644 (file)
index 0000000..e1c06ac
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FLOWER_H__
+#define __FLOWER_H__
+
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "player_status.hpp"
+
+class Flower : public MovingObject
+{
+public:
+  Flower(BonusType type);
+  ~Flower();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+  BonusType type;
+  Sprite* sprite;
+};
+
+#endif
diff --git a/src/object/gameobjs.cpp b/src/object/gameobjs.cpp
new file mode 100644 (file)
index 0000000..1eed690
--- /dev/null
@@ -0,0 +1,177 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <algorithm>
+#include <iostream>
+#include <cmath>
+
+#include "tile.hpp"
+#include "tile_manager.hpp"
+#include "game_session.hpp"
+#include "gameobjs.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "tilemap.hpp"
+#include "video/drawing_context.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "random_generator.hpp"
+
+BouncyCoin::BouncyCoin(const Vector& pos)
+  : position(pos)
+{
+  timer.start(.3f);
+  sprite = sprite_manager->create("images/objects/coin/coin.sprite");
+  sprite->set_action("still");
+}
+
+BouncyCoin::~BouncyCoin()
+{
+  delete sprite;
+}
+
+void
+BouncyCoin::update(float elapsed_time)
+{
+  position.y += -200 * elapsed_time;
+
+  if(timer.check())
+    remove_me();
+}
+
+void
+BouncyCoin::draw(DrawingContext& context)
+{
+  sprite->draw(context, position, LAYER_OBJECTS + 5);
+}
+
+//---------------------------------------------------------------------------
+
+BrokenBrick::BrokenBrick(Sprite* nsprite,
+    const Vector& pos, const Vector& nmovement)
+  : sprite(new Sprite(*nsprite)), position(pos), movement(nmovement)
+{
+  timer.start(.2f);
+}
+
+BrokenBrick::~BrokenBrick()
+{
+  delete sprite;
+}
+
+void
+BrokenBrick::update(float elapsed_time)
+{
+  position += movement * elapsed_time;
+
+  if (timer.check())
+    remove_me();
+}
+
+void
+BrokenBrick::draw(DrawingContext& context)
+{
+  sprite->draw_part(context,
+      Vector(systemRandom.rand(16), systemRandom.rand(16)), Vector(16, 16),
+      position, LAYER_OBJECTS + 1);
+}
+
+//---------------------------------------------------------------------------
+
+FloatingText::FloatingText(const Vector& pos, const std::string& text_)
+  : position(pos), text(text_)
+{
+  timer.start(.1f);
+  position.x -= text.size() * 8;
+}
+
+FloatingText::FloatingText(const Vector& pos, int score)
+  : position(pos)
+{
+  timer.start(.1f);
+
+  // turn int into a string
+  char str[10];
+  snprintf(str, 10, "%d", score);
+  text = str;
+
+  position.x -= text.size() * 8;
+}
+
+void
+FloatingText::update(float elapsed_time)
+{
+  position.y -= 1.4 * elapsed_time;
+
+  if(timer.check())
+    remove_me();
+}
+
+#define FADING_TIME .350
+
+void
+FloatingText::draw(DrawingContext& context)
+{
+  // make an alpha animation when disapearing
+  int alpha;
+  if(timer.get_timeleft() < FADING_TIME)
+    alpha = int(timer.get_timeleft() * 255 / FADING_TIME);
+  else
+    alpha = 255;
+
+  context.push_transform();
+  context.set_alpha(alpha);
+
+  context.draw_text(gold_text, text, position, ALIGN_LEFT, LAYER_OBJECTS+1);
+
+  context.pop_transform();
+}
+
+Sprite *img_smoke_cloud = 0;
+
+SmokeCloud::SmokeCloud(const Vector& pos)
+  : position(pos)
+{
+  timer.start(.3f);
+  sprite = sprite_manager->create("images/objects/particles/stomp.sprite");
+}
+
+SmokeCloud::~SmokeCloud()
+{
+  delete sprite;
+}
+
+void
+SmokeCloud::update(float elapsed_time)
+{
+  position.y -= 120 * elapsed_time;
+
+  if(timer.check())
+    remove_me();
+}
+
+void
+SmokeCloud::draw(DrawingContext& context)
+{
+  sprite->draw(context, position, LAYER_OBJECTS+1);
+}
diff --git a/src/object/gameobjs.hpp b/src/object/gameobjs.hpp
new file mode 100644 (file)
index 0000000..5a4063f
--- /dev/null
@@ -0,0 +1,100 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_GAMEOBJS_H
+#define SUPERTUX_GAMEOBJS_H
+
+#include "video/surface.hpp"
+#include "timer.hpp"
+#include "game_object.hpp"
+#include "moving_object.hpp"
+#include "serializable.hpp"
+#include "video/color.hpp"
+
+/* Bounciness of distros: */
+#define NO_BOUNCE 0
+#define BOUNCE 1
+
+class Sprite;
+
+class BouncyCoin : public GameObject
+{
+public:
+  BouncyCoin(const Vector& pos);
+  ~BouncyCoin();
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Sprite* sprite;
+  Vector position;
+  Timer timer;
+};
+
+class BrokenBrick : public GameObject
+{
+public:
+  BrokenBrick(Sprite* sprite, const Vector& pos, const Vector& movement);
+  ~BrokenBrick();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Timer timer;
+  Sprite* sprite;
+  Vector position;
+  Vector movement;
+};
+
+class FloatingText : public GameObject
+{
+public:
+  FloatingText(const Vector& pos, const std::string& text_);
+  FloatingText(const Vector& pos, int s);  // use this for score, for instance
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Vector position;
+  std::string text;
+  Timer timer;
+};
+
+class SmokeCloud : public GameObject
+{
+public:
+  SmokeCloud(const Vector& pos);
+  ~SmokeCloud();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Sprite* sprite;
+  Timer timer;
+  Vector position;
+};
+
+#endif
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
diff --git a/src/object/gradient.cpp b/src/object/gradient.cpp
new file mode 100644 (file)
index 0000000..d89996b
--- /dev/null
@@ -0,0 +1,103 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include "gradient.hpp"
+#include "camera.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+Gradient::Gradient()
+  : layer(LAYER_BACKGROUND0)
+{
+}
+
+Gradient::Gradient(const lisp::Lisp& reader)
+  : layer(LAYER_BACKGROUND0)
+{
+  reader.get("layer", layer);
+  std::vector<float> bkgd_top_color, bkgd_bottom_color;
+  if(!reader.get_vector("top_color", bkgd_top_color) ||
+     !reader.get_vector("bottom_color", bkgd_bottom_color))
+    throw std::runtime_error("Must specify top_color and bottom_color in gradient");
+
+  gradient_top = Color(bkgd_top_color);
+  gradient_bottom = Color(bkgd_bottom_color);
+}
+
+Gradient::~Gradient()
+{
+}
+
+void
+Gradient::write(lisp::Writer& writer)
+{
+  writer.start_list("gradient");
+
+  std::vector<float> bkgd_top_color, bkgd_bottom_color;
+  bkgd_top_color.push_back(gradient_top.red);
+  bkgd_top_color.push_back(gradient_top.green);
+  bkgd_top_color.push_back(gradient_top.blue);
+  bkgd_bottom_color.push_back(gradient_bottom.red);
+  bkgd_bottom_color.push_back(gradient_bottom.green);
+  bkgd_bottom_color.push_back(gradient_bottom.blue);
+  writer.write_float_vector("top_color", bkgd_top_color);
+  writer.write_float_vector("bottom_color", bkgd_bottom_color);
+
+  writer.write_int("layer", layer);
+
+  writer.end_list("gradient");
+}
+
+void
+Gradient::update(float)
+{
+}
+
+void
+Gradient::set_gradient(Color top, Color bottom)
+{
+  gradient_top = top;
+  gradient_bottom = bottom;
+
+  if (gradient_top.red > 1.0 || gradient_top.green > 1.0
+   || gradient_top.blue > 1.0 || gradient_top.alpha > 1.0)
+    log_warning << "top gradient color has values above 1.0" << std::endl;
+  if (gradient_bottom.red > 1.0 || gradient_bottom.green > 1.0
+   || gradient_bottom.blue > 1.0 || gradient_bottom.alpha > 1.0)
+    log_warning << "bottom gradient color has values above 1.0" << std::endl;
+}
+
+void
+Gradient::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+  context.draw_gradient(gradient_top, gradient_bottom, layer);
+  context.pop_transform();
+}
+
+IMPLEMENT_FACTORY(Gradient, "gradient");
diff --git a/src/object/gradient.hpp b/src/object/gradient.hpp
new file mode 100644 (file)
index 0000000..6feba31
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_GRADIENT_H
+#define SUPERTUX_GRADIENT_H
+
+#include <memory>
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+
+class DisplayManager;
+
+namespace lisp {
+class Lisp;
+}
+
+class Gradient : public GameObject, public Serializable
+{
+public:
+  Gradient();
+  Gradient(const lisp::Lisp& reader);
+  virtual ~Gradient();
+
+  virtual void write(lisp::Writer& writer);
+
+  void set_gradient(Color top, Color bottom);
+
+  Color get_gradient_top() const
+  { return gradient_top; }
+
+  Color get_gradient_bottom() const
+  { return gradient_bottom; }
+
+  virtual void update(float elapsed_time);
+
+  virtual void draw(DrawingContext& context);
+
+private:
+  int layer;
+  Color gradient_top, gradient_bottom;
+};
+
+#endif /*SUPERTUX_BACKGROUND_H*/
diff --git a/src/object/growup.cpp b/src/object/growup.cpp
new file mode 100644 (file)
index 0000000..1d256db
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include "growup.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+
+GrowUp::GrowUp(Direction direction)
+       : MovingSprite(Vector(0,0), "images/powerups/egg/egg.sprite", LAYER_OBJECTS, COLGROUP_MOVING)
+{
+  physic.enable_gravity(true);
+  physic.set_velocity_x((direction == LEFT)?-100:100);
+  sound_manager->preload("sounds/grow.wav");
+}
+
+void
+GrowUp::update(float elapsed_time)
+{
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+GrowUp::collision_solid(const CollisionHit& hit)
+{
+  if(hit.top || hit.bottom)
+    physic.set_velocity_y(0);
+  if(hit.left || hit.right)
+    physic.set_velocity_x(-physic.get_velocity_x());
+}
+
+HitResponse
+GrowUp::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if(player != 0) {
+    if(!player->add_bonus(GROWUP_BONUS, true))
+      return FORCE_MOVE;
+
+    sound_manager->play("sounds/grow.wav");
+    remove_me();
+
+    return ABORT_MOVE;
+  }
+
+  return FORCE_MOVE;
+}
diff --git a/src/object/growup.hpp b/src/object/growup.hpp
new file mode 100644 (file)
index 0000000..869b3fe
--- /dev/null
@@ -0,0 +1,38 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __GROWUP_H__
+#define __GROWUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class GrowUp : public MovingSprite, private UsesPhysic
+{
+public:
+  GrowUp(Direction direction = RIGHT);
+  virtual GrowUp* clone() const { return new GrowUp(*this); }
+
+  virtual void update(float elapsed_time);
+  virtual void collision_solid(const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
diff --git a/src/object/hurting_platform.cpp b/src/object/hurting_platform.cpp
new file mode 100644 (file)
index 0000000..59cbfdf
--- /dev/null
@@ -0,0 +1,51 @@
+//  $Id$
+//
+//  SuperTux - Hurting Platform
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <stdexcept>
+
+#include "hurting_platform.hpp"
+
+#include "log.hpp"
+#include "player.hpp"
+#include "badguy/badguy.hpp"
+#include "object_factory.hpp"
+
+HurtingPlatform::HurtingPlatform(const lisp::Lisp& reader)
+       : Platform(reader)
+{
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+HitResponse
+HurtingPlatform::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if (player) {
+    player->kill(false);
+  }
+  BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
+  if (badguy) {
+    badguy->kill_fall();
+  }
+
+  return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(HurtingPlatform, "hurting_platform");
diff --git a/src/object/hurting_platform.hpp b/src/object/hurting_platform.hpp
new file mode 100644 (file)
index 0000000..71cbf84
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux - Hurting Platform
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __HURTING_PLATFORM_H__
+#define __HURTING_PLATFORM_H__
+
+#include <memory>
+#include "object/platform.hpp"
+
+/**
+ * Platform that hurts Tux and Badguys when touched
+ */
+class HurtingPlatform : public Platform
+{
+public:
+  HurtingPlatform(const lisp::Lisp& reader);
+  virtual HurtingPlatform* clone() const { return new HurtingPlatform(*this); }
+
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+
+};
+
+#endif
diff --git a/src/object/infoblock.cpp b/src/object/infoblock.cpp
new file mode 100644 (file)
index 0000000..dda84e6
--- /dev/null
@@ -0,0 +1,188 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "infoblock.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "object_factory.hpp"
+#include "lisp/lisp.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+#include "object/player.hpp"
+#include "main.hpp"
+
+namespace {
+  const float SCROLL_DELAY = 0.5;
+  const float SCROLL_DISTANCE = 16;
+  const float WIDTH = 400;
+  const float HEIGHT = 200;
+}
+
+InfoBlock::InfoBlock(const lisp::Lisp& lisp)
+  : Block(sprite_manager->create("images/objects/bonus_block/infoblock.sprite")), shown_pct(0), dest_pct(0)
+{
+  Vector pos;
+  lisp.get("x", pos.x);
+  lisp.get("y", pos.y);
+  bbox.set_pos(pos);
+
+  if(!lisp.get("message", message)) {
+    log_warning << "No message in InfoBlock" << std::endl;
+  }
+  //stopped = false;
+  //ringing = new AmbientSound(get_pos(), 0.5, 300, 1, "sounds/phone.wav");
+  //Sector::current()->add_object(ringing);
+
+  // Split text string lines into a vector
+  lines = InfoBoxLine::split(message, 400);
+  lines_height = 0;
+  for(size_t i = 0; i < lines.size(); ++i) lines_height+=lines[i]->get_height();
+}
+
+InfoBlock::~InfoBlock()
+{
+  for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) {
+    delete *i;
+  }
+}
+
+void
+InfoBlock::hit(Player& )
+{
+  start_bounce();
+
+  //if (!stopped) {
+  //  ringing->remove_me();
+  //  stopped = true;
+  //}
+
+  if (dest_pct != 1) {
+
+    // first hide all other InfoBlocks' messages in same sector
+    Sector* parent = Sector::current();
+    if (!parent) return;
+    for (Sector::GameObjects::iterator i = parent->gameobjects.begin(); i != parent->gameobjects.end(); i++) {
+      InfoBlock* block = dynamic_cast<InfoBlock*>(*i);
+      if (!block) continue;
+      if (block != this) block->hide_message();
+    }
+
+    // show our message
+    show_message();
+
+  } else {
+    hide_message();
+  }
+}
+
+Player*
+InfoBlock::get_nearest_player()
+{
+  // FIXME: does not really return nearest player
+
+  std::vector<Player*> players = Sector::current()->get_players();
+  for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+    Player* player = *playerIter;
+    return player;
+  }
+
+  return 0;
+}
+
+void
+InfoBlock::update(float delta)
+{
+  Block::update(delta);
+
+  if (delta == 0) return;
+
+  // hide message if player is too far away or above infoblock
+  if (dest_pct > 0) {
+    Player* player = get_nearest_player();
+    if (player) {
+      Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
+      Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
+      Vector dist = (p2 - p1);
+      float d = dist.norm();
+      if (d > 128 || dist.y < 0) dest_pct = 0;
+    }
+  }
+
+  // handle soft fade-in and fade-out
+  if (shown_pct != dest_pct) {
+    if (dest_pct > shown_pct) shown_pct = std::min(shown_pct + 2*delta, dest_pct);
+    if (dest_pct < shown_pct) shown_pct = std::max(shown_pct - 2*delta, dest_pct);
+  }
+}
+
+void
+InfoBlock::draw(DrawingContext& context)
+{
+  Block::draw(context);
+
+  if (shown_pct <= 0) return;
+
+  context.push_transform();
+  //context.set_translation(Vector(0, 0));
+  context.set_alpha(shown_pct);
+
+  //float x1 = SCREEN_WIDTH/2-200;
+  //float y1 = SCREEN_HEIGHT/2-200;
+  float border = 8;
+  float width = 400; // this is the text width only
+  float height = lines_height; // this is the text height only
+  float x1 = (get_bbox().p1.x + get_bbox().p2.x)/2 - width/2;
+  float x2 = (get_bbox().p1.x + get_bbox().p2.x)/2 + width/2;
+  float y1 = original_y - height;
+
+  // lines_height includes one ITEMS_SPACE too much, so the bottom border is reduced by 4px
+  context.draw_filled_rect(Vector(x1-border, y1-border), Vector(width+2*border, height+2*border-4), Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-50);
+
+  float y = y1;
+  for(size_t i = 0; i < lines.size(); ++i) {
+    if(y >= y1 + height) {
+      //log_warning << "Too many lines of text in InfoBlock" << std::endl;
+      //dest_pct = 0;
+      //shown_pct = 0;
+      break;
+    }
+
+    lines[i]->draw(context, Rect(x1, y, x2, y), LAYER_GUI-50+1);
+    y += lines[i]->get_height();
+  }
+
+  context.pop_transform();
+}
+
+void
+InfoBlock::show_message()
+{
+  dest_pct = 1;
+}
+
+void
+InfoBlock::hide_message()
+{
+  dest_pct = 0;
+}
+
+IMPLEMENT_FACTORY(InfoBlock, "infoblock")
diff --git a/src/object/infoblock.hpp b/src/object/infoblock.hpp
new file mode 100644 (file)
index 0000000..21e2ae4
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __INFOBLOCK_H__
+#define __INFOBLOCK_H__
+
+#include "block.hpp"
+//#include "object/ambient_sound.hpp"
+#include "textscroller.hpp"
+
+class InfoBlock : public Block
+{
+public:
+  InfoBlock(const lisp::Lisp& lisp);
+  virtual ~InfoBlock();
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+  void show_message();
+  void hide_message();
+
+protected:
+  virtual void hit(Player& player);
+  std::string message;
+  //AmbientSound* ringing;
+  //bool stopped;
+  float shown_pct; /**< Value in the range of 0..1, depending on how much of the infobox is currently shown */
+  float dest_pct; /**< With each call to update(), shown_pct will slowly transition to this value */
+
+  Player* get_nearest_player();
+
+  std::vector<InfoBoxLine*> lines; /**< lines of text (or images) to display */
+  float lines_height;
+};
+
+#endif
diff --git a/src/object/invisible_block.cpp b/src/object/invisible_block.cpp
new file mode 100644 (file)
index 0000000..2e802fc
--- /dev/null
@@ -0,0 +1,83 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "invisible_block.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+
+InvisibleBlock::InvisibleBlock(const Vector& pos)
+  : Block(sprite_manager->create("images/objects/bonus_block/invisibleblock.sprite")), visible(false)
+{
+  bbox.set_pos(pos);
+  sound_manager->preload("sounds/brick.wav");
+  sound_manager->preload("sounds/brick.wav");
+}
+
+void
+InvisibleBlock::draw(DrawingContext& context)
+{
+  if(visible)
+    sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+bool
+InvisibleBlock::collides(GameObject& other, const CollisionHit& )
+{
+  if(visible)
+    return true;
+
+  // if we're not visible, only register a collision if this will make us visible
+  Player* player = dynamic_cast<Player*> (&other);
+  if ((player) 
+          && (player->get_movement().y <= 0)
+          && (player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0)) {
+    return true;
+  }
+
+  return false;
+}
+
+HitResponse
+InvisibleBlock::collision(GameObject& other, const CollisionHit& hit)
+{
+  return Block::collision(other, hit);
+}
+
+void
+InvisibleBlock::hit(Player& )
+{
+  sound_manager->play("sounds/brick.wav");
+
+  if(visible)
+    return;
+
+  sprite->set_action("empty");
+  start_bounce();
+  set_group(COLGROUP_STATIC);
+  visible = true;
+}
+
+//IMPLEMENT_FACTORY(InvisibleBlock, "invisible_block");
diff --git a/src/object/invisible_block.hpp b/src/object/invisible_block.hpp
new file mode 100644 (file)
index 0000000..85fbb4b
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __INVISIBLE_BLOCK_H__
+#define __INVISIBLE_BLOCK_H__
+
+#include "block.hpp"
+
+class InvisibleBlock : public Block
+{
+public:
+  InvisibleBlock(const Vector& pos);
+
+  virtual void draw(DrawingContext& context);
+  virtual bool collides(GameObject& other, const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+  virtual void hit(Player& player);
+
+private:
+  bool visible;
+};
+
+#endif
diff --git a/src/object/invisible_wall.cpp b/src/object/invisible_wall.cpp
new file mode 100644 (file)
index 0000000..4416b23
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "invisible_wall.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+
+InvisibleWall::InvisibleWall(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/invisible/invisible.sprite", LAYER_TILES, COLGROUP_STATIC), width(32), height(32)
+{
+       lisp.get("width", width);
+       lisp.get("height", height);
+       bbox.set_size(width, height);
+}
+
+HitResponse
+InvisibleWall::collision(GameObject& , const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(InvisibleWall, "invisible_wall");
diff --git a/src/object/invisible_wall.hpp b/src/object/invisible_wall.hpp
new file mode 100644 (file)
index 0000000..0daa737
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __INVISIBLE_WALL_H__
+#define __INVISIBLE_WALL_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+class Player;
+
+/** A tile that starts falling down if tux stands to long on it */
+class InvisibleWall : public MovingSprite, private UsesPhysic
+{
+public:
+  InvisibleWall(const lisp::Lisp& lisp);
+  virtual InvisibleWall* clone() const { return new InvisibleWall(*this); }
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+  float width, height;
+};
+
+#endif
diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp
new file mode 100644 (file)
index 0000000..fb26842
--- /dev/null
@@ -0,0 +1,227 @@
+//  $Id$
+//
+//  SuperTux - Ispy
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "ispy.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player.hpp"
+#include "object_factory.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+Ispy::Ispy(const lisp::Lisp& reader)
+       : MovingSprite(reader, "images/objects/ispy/ispy.sprite", LAYER_TILES+5, COLGROUP_DISABLED), state(ISPYSTATE_IDLE), dir(AUTO)
+{
+  // read script to execute
+  reader.get("script", script);
+
+  // read direction to face in
+  std::string dir_str;
+  bool facing_down = false;
+  reader.get("direction", dir_str);
+  if( dir_str == "left" ) dir = LEFT;
+  if( dir_str == "right" ) dir = RIGHT;
+  reader.get("facing-down", facing_down);
+  if (facing_down) dir = DOWN;
+  if (dir == AUTO) log_warning << "Setting an Ispy's direction to AUTO is no good idea" << std::endl;
+
+  // set initial sprite action
+  sprite->set_action((dir == DOWN) ? "idle-down" : ((dir == LEFT) ? "idle-left" : "idle-right"));
+}
+
+void
+Ispy::write(lisp::Writer& writer)
+{
+  writer.start_list("ispy");
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_string("script", script);
+  switch (dir)
+  {
+    case DOWN:
+      writer.write_string("direction", "down"); break;
+    case LEFT:
+      writer.write_string("direction", "left"); break;
+    case RIGHT:
+      writer.write_string("direction", "right"); break;
+    default: break;
+  }
+  writer.end_list("ispy");
+}
+
+HitResponse
+Ispy::collision(GameObject& , const CollisionHit& )
+{
+  return ABORT_MOVE;
+}
+
+bool
+Ispy::line_intersects_line(Vector line1_start, Vector line1_end, Vector line2_start, Vector line2_end) {
+  // Adapted from Striker, (C) 1999 Joris van der Hoeven, GPL
+
+  float a1 = line1_start.x, b1 = line1_start.y, a2 = line1_end.x, b2 = line1_end.y;
+  float c1 = line2_start.x, d1 = line2_start.y, c2 = line2_end.x, d2 = line2_end.y;
+
+  float num = (b2-b1)*(c2-c1) - (a2-a1)*(d2-d1);
+  float den1 = (d2-b2)*(c1-c2) + (a2-c2)*(d1-d2);
+  float den2 = (d2-b2)*(a1-a2) + (a2-c2)*(b1-b2);
+
+  // normalize to positive numerator
+  if (num < 0) { 
+    num =- num; 
+    den1 =- den1; 
+    den2 =- den2; 
+  }
+
+  // numerator is zero -> Check for parallel or coinciding lines
+  if (num == 0) {
+    if ((b1-b2)*(c1-a2) != (a1-a2)*(d1-b2)) return false;
+    if (a1 == a2) { 
+      std::swap(a1, b1); 
+      std::swap(a2, b2); 
+      std::swap(c1, d1); 
+      std::swap(c2, d2); 
+    }
+    if (a1 > a2) std::swap(a1, a2);
+    if (c1 > c2) std::swap(c1, c2);
+    return ((a1 <= c2) && (a2 >= c1));
+  }
+
+  // Standard check
+  return (den1>=0) && (den1<=num) && (den2>=0) && (den2<=num);
+
+}
+
+bool
+Ispy::intersects_line(Rect r, Vector line_start, Vector line_end)
+{
+  Vector p1 = r.p1;
+  Vector p2 = Vector(r.p2.x, r.p1.y);
+  Vector p3 = r.p2;
+  Vector p4 = Vector(r.p1.x, r.p2.y);
+  if (line_intersects_line(p1, p2, line_start, line_end)) return true;
+  if (line_intersects_line(p2, p3, line_start, line_end)) return true;
+  if (line_intersects_line(p3, p4, line_start, line_end)) return true;
+  if (line_intersects_line(p4, p1, line_start, line_end)) return true;
+  return false;
+}
+
+bool
+Ispy::free_line_of_sight(Vector line_start, Vector line_end, const MovingObject* ignore_object)
+{
+
+  // check if no tile is in the way
+  float lsx = std::min(line_start.x, line_end.x);
+  float lex = std::max(line_start.x, line_end.x);
+  float lsy = std::min(line_start.y, line_end.y);
+  float ley = std::max(line_start.y, line_end.y);
+  std::list<TileMap*> solid_tilemaps = Sector::current()->solid_tilemaps;
+  for (float test_x = lsx; test_x <= lex; test_x += 16) {
+    for (float test_y = lsy; test_y <= ley; test_y += 16) {
+      for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+        TileMap* solids = *i;
+       const Tile* tile = solids->get_tile_at(Vector(test_x, test_y));
+       if(!tile) continue;
+        // FIXME: check collision with slope tiles
+       if((tile->getAttributes() & Tile::SOLID)) return false;
+      }
+    }
+  }
+
+  // check if no object is in the way
+  using namespace collision;
+  Sector::MovingObjects& moving_objects = Sector::current()->moving_objects;
+  for(Sector::MovingObjects::const_iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    const MovingObject* moving_object = *i;
+    if (moving_object == ignore_object) continue;
+    if (!moving_object->is_valid()) continue;
+    if ((moving_object->get_group() == COLGROUP_MOVING)
+      || (moving_object->get_group() == COLGROUP_MOVING_STATIC)
+      || (moving_object->get_group() == COLGROUP_STATIC)) {
+      if(intersects_line(moving_object->get_bbox(), line_start, line_end)) return false;
+    }
+  }
+
+  return true;
+}
+
+void 
+Ispy::update(float )
+{
+
+  if (state == ISPYSTATE_IDLE) {
+    // check if a player has been spotted
+    bool playerSpotted = false;
+    std::vector<Player*> players = Sector::current()->get_players();
+    for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+      Player* player = *playerIter;
+
+      Vector eye = get_bbox().get_middle();
+      if (dir == LEFT) eye = Vector(get_bbox().p1.x, get_bbox().get_middle().y);
+      if (dir == RIGHT) eye = Vector(get_bbox().p2.x, get_bbox().get_middle().y);
+      if (dir == UP) eye = Vector(get_bbox().get_middle().x, get_bbox().p1.y);
+      if (dir == DOWN) eye = Vector(get_bbox().get_middle().x, get_bbox().p2.y);
+
+      // test for free line of sight to any of all four corners and the middle of a player's bounding box
+      if (free_line_of_sight(eye, player->get_bbox().p1, player)) playerSpotted = true;
+      if (free_line_of_sight(eye, Vector(player->get_bbox().p2.x, player->get_bbox().p1.y), player)) playerSpotted = true;
+      if (free_line_of_sight(eye, player->get_bbox().p2, player)) playerSpotted = true;
+      if (free_line_of_sight(eye, Vector(player->get_bbox().p1.x, player->get_bbox().p2.y), player)) playerSpotted = true;
+      if (free_line_of_sight(eye, player->get_bbox().get_middle(), player)) playerSpotted = true;
+    }
+
+    if (playerSpotted) {
+      sprite->set_action((dir == DOWN) ? "alert-down" : ((dir == LEFT) ? "alert-left" : "alert-right"), 1);
+      state = ISPYSTATE_ALERT;
+
+      std::istringstream stream(script);
+      Sector::current()->run_script(stream, "Ispy");
+    }
+  }
+  if (state == ISPYSTATE_ALERT) {
+    if (sprite->animation_done()) {
+      sprite->set_action((dir == DOWN) ? "hiding-down" : ((dir == LEFT) ? "hiding-left" : "hiding-right"), 1);
+      state = ISPYSTATE_HIDING;
+    }
+  }
+  if (state == ISPYSTATE_HIDING) {
+    if (sprite->animation_done()) {
+      sprite->set_action((dir == DOWN) ? "showing-down" : ((dir == LEFT) ? "showing-left" : "showing-right"), 1);
+      state = ISPYSTATE_SHOWING;
+    }
+  }
+  if (state == ISPYSTATE_SHOWING) {
+    if (sprite->animation_done()) {
+      sprite->set_action((dir == DOWN) ? "idle-down" : ((dir == LEFT) ? "idle-left" : "idle-right"));
+      state = ISPYSTATE_IDLE;
+    }
+  }
+}
+
+IMPLEMENT_FACTORY(Ispy, "ispy");
+
+
diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp
new file mode 100644 (file)
index 0000000..4fc2c1b
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux - Ispy
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __OBJECT_ISPY_H__
+#define __OBJECT_ISPY_H__
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "serializable.hpp"
+#include "badguy/badguy.hpp"
+#include "direction.hpp"
+
+/**
+ * An Ispy: When it spots Tux, a script will run.
+ */
+class Ispy : public MovingSprite, public Serializable
+{
+public:
+  Ispy(const lisp::Lisp& lisp);
+
+  void write(lisp::Writer& writer);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  virtual void update(float elapsed_time);
+
+private:
+  bool line_intersects_line(Vector line1_start, Vector line1_end, Vector line2_start, Vector line2_end);
+  bool intersects_line(Rect r, Vector line_start, Vector line_end);
+  bool free_line_of_sight(Vector p1, Vector p2, const MovingObject* ignore_object);
+
+  enum IspyState {
+    ISPYSTATE_IDLE,
+    ISPYSTATE_ALERT,
+    ISPYSTATE_HIDING,
+    ISPYSTATE_SHOWING
+  };
+  IspyState state; /**< current state */
+
+  std::string script; /**< script to execute when Tux is spotted */
+  Direction dir;
+
+};
+
+#endif
+
diff --git a/src/object/lantern.cpp b/src/object/lantern.cpp
new file mode 100644 (file)
index 0000000..8812e4b
--- /dev/null
@@ -0,0 +1,134 @@
+//  $Id$
+//
+//  SuperTux - Lantern
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "lantern.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "object_factory.hpp"
+#include "badguy/willowisp.hpp"
+#include "badguy/treewillowisp.hpp"
+
+Lantern::Lantern(const lisp::Lisp& reader)
+  : Rock(reader, "images/objects/lantern/lantern.sprite"),
+    lightcolor(1.0f, 1.0f, 1.0f)
+{
+  //get color from lisp
+  std::vector<float> vColor;
+  reader.get_vector("color", vColor);
+  lightcolor = Color(vColor);
+  lightsprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+  lightsprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+  updateColor();
+  sound_manager->preload("sounds/willocatch.wav");
+}
+
+Lantern::Lantern(const Vector& pos)
+  : Rock(pos, "images/objects/lantern/lantern.sprite"),
+    lightcolor(0.0f, 0.0f, 0.0f)
+{
+  lightsprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+  lightsprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+  updateColor();
+  sound_manager->preload("sounds/willocatch.wav");
+}
+
+Lantern::~Lantern()
+{
+  delete lightsprite;
+}
+
+void
+Lantern::updateColor(){
+  lightsprite->set_color(lightcolor);
+  //Turn lantern off if light is black
+  if(lightcolor.red == 0 && lightcolor.green == 0 && lightcolor.blue == 0){
+     sprite->set_action("off");
+  } else {
+     sprite->set_action("normal");
+     sprite->set_color(lightcolor);
+  }
+}
+
+void
+Lantern::draw(DrawingContext& context){
+  //Draw the Sprite.
+  MovingSprite::draw(context);
+  //Let there be light.
+  context.push_target();
+  context.set_target(DrawingContext::LIGHTMAP);
+
+  lightsprite->draw(context, get_bbox().get_middle(), 0);
+
+  context.pop_target();
+}
+
+HitResponse Lantern::collision(GameObject& other, const CollisionHit& hit) {
+  if (is_open()) {
+    WillOWisp* wow = dynamic_cast<WillOWisp*>(&other);
+    if (wow) {
+      // collided with WillOWisp while grabbed and unlit
+      sound_manager->play("sounds/willocatch.wav");
+      lightcolor = Color(0,1,0);
+      updateColor();
+      wow->vanish();
+    }
+    TreeWillOWisp* twow = dynamic_cast<TreeWillOWisp*>(&other);
+    if (twow) {
+      // collided with TreeWillOWisp while grabbed and unlit
+      sound_manager->play("sounds/willocatch.wav");
+      lightcolor = twow->get_color();
+      updateColor();
+      twow->vanish();
+    }
+  }
+  return Rock::collision(other, hit);
+}
+
+void
+Lantern::grab(MovingObject& object, const Vector& pos, Direction dir)
+{
+  Rock::grab(object, pos, dir);
+
+  // if lantern is not lit, draw it as opened
+  if (is_open()) {
+    sprite->set_action("off-open");
+  }
+
+}
+
+void
+Lantern::ungrab(MovingObject& object, Direction dir)
+{
+  // if lantern is not lit, it was drawn as opened while grabbed. Now draw it as closed again
+  if (is_open()) {
+    sprite->set_action("off");
+  }
+
+  Rock::ungrab(object, dir);
+}
+  
+bool 
+Lantern::is_open()
+{
+  return ((grabbed) && lightcolor.red == 0 && lightcolor.green == 0 && lightcolor.blue == 0);
+}
+
+IMPLEMENT_FACTORY(Lantern, "lantern");
+
diff --git a/src/object/lantern.hpp b/src/object/lantern.hpp
new file mode 100644 (file)
index 0000000..b8498a3
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  SuperTux - Lantern
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SUPERTUX_LANTERN_H__
+#define __SUPERTUX_LANTERN_H___
+
+#include "object/moving_sprite.hpp"
+#include "object/rock.hpp"
+
+/**
+ * Lantern. A portable Light Source.
+ */
+class Lantern : public Rock
+{
+public:
+  Lantern(const Vector& pos);
+  Lantern(const lisp::Lisp& reader);
+  void draw(DrawingContext& context);
+  ~Lantern();
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  void grab(MovingObject& object, const Vector& pos, Direction dir);
+  void ungrab(MovingObject& object, Direction dir);
+
+  /**
+   * returns true if lamp is currently open
+   */
+  bool is_open();
+
+  /**
+   * returns the lamp's color
+   */
+  Color get_color() const {
+    return lightcolor;
+  }
+
+private:
+  Color lightcolor;
+  Sprite* lightsprite;
+  void updateColor();
+};
+
+#endif
diff --git a/src/object/level_time.cpp b/src/object/level_time.cpp
new file mode 100644 (file)
index 0000000..92a5a90
--- /dev/null
@@ -0,0 +1,132 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "level_time.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+#include "scripting/level_time.hpp"
+#include "scripting/squirrel_util.hpp"
+
+/** When to alert player they're low on time! */
+static const float TIME_WARNING = 20;
+
+LevelTime::LevelTime(const lisp::Lisp& reader)
+: running(true), time_left(0)
+{
+  reader.get("name", name);
+  reader.get("time", time_left);
+  if(time_left <= 0) throw std::runtime_error("No or invalid leveltime specified");
+  time_surface.reset(new Surface("images/engine/hud/time-0.png"));
+}
+
+void
+LevelTime::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::LevelTime* interface = new Scripting::LevelTime(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+LevelTime::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+LevelTime::update(float elapsed_time)
+{
+  if (!running) return;
+
+  int prev_time = (int) floor(time_left*5);
+  time_left -= elapsed_time;
+  if(time_left <= 0) {
+    if(time_left <= -5 || !Sector::current()->player->get_coins())
+    {
+      Sector::current()->player->kill(true);
+      stop();
+    }
+    if(prev_time != (int) floor(time_left*5))
+    {
+      Sector::current()->player->add_coins(-1);
+    }
+  }
+}
+
+void
+LevelTime::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  if ((time_left > TIME_WARNING) || (int(game_time * 2.5) % 2)) {
+    std::stringstream ss;
+    ss << int(time_left);
+    std::string time_text = ss.str();
+
+    Surface* time_surf = time_surface.get();
+    if (time_surf) {
+      float all_width = time_surf->get_width() + white_text->get_text_width(time_text);
+      context.draw_surface(time_surf, Vector((SCREEN_WIDTH - all_width)/2, BORDER_Y + 1), LAYER_FOREGROUND1);
+      context.draw_text(gold_text, time_text, Vector((SCREEN_WIDTH - all_width)/2 + time_surf->get_width(), BORDER_Y), ALIGN_LEFT, LAYER_FOREGROUND1);
+    }
+  }
+
+  context.pop_transform();
+}
+
+void
+LevelTime::start()
+{
+  running = true;
+}
+
+void
+LevelTime::stop()
+{
+  running = false;
+}
+
+float
+LevelTime::get_time()
+{
+  return time_left;
+}
+
+void
+LevelTime::set_time(float time_left)
+{
+  this->time_left = std::min(std::max(time_left, 0.0f), 999.0f);
+}
+
+IMPLEMENT_FACTORY(LevelTime, "leveltime");
diff --git a/src/object/level_time.hpp b/src/object/level_time.hpp
new file mode 100644 (file)
index 0000000..2ef9c8c
--- /dev/null
@@ -0,0 +1,76 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LEVELTIME_H__
+#define __LEVELTIME_H__
+
+#include <memory>
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "video/surface.hpp"
+#include "script_interface.hpp"
+
+class LevelTime : public GameObject, public ScriptInterface
+{
+public:
+    LevelTime(const lisp::Lisp& reader);
+
+    virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+    virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+    void update(float elapsed_time);
+    void draw(DrawingContext& context);
+
+    /**
+     * @name Scriptable Methods
+     * @{
+     */
+
+    /**
+     * Resumes the countdown
+     */
+    void start();
+
+    /**
+     * Pauses the countdown
+     */
+    void stop();
+
+    /**
+     * Returns the number of seconds left on the clock
+     */
+    float get_time();
+
+    /**
+     * Changes the number of seconds left on the clock
+     */
+    void set_time(float time_left);
+
+    /**
+     * @}
+     */
+
+private:
+    std::auto_ptr<Surface> time_surface;
+    bool running;
+    float time_left;
+};
+
+#endif
diff --git a/src/object/light.cpp b/src/object/light.cpp
new file mode 100644 (file)
index 0000000..a0abfcf
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "light.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+
+Light::Light(const Vector& center, const Color& color) : position(center), color(color)
+{
+  sprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+}
+
+Light::~Light()
+{
+  delete sprite;
+}
+
+void
+Light::update(float )
+{
+}
+
+void
+Light::draw(DrawingContext& context)
+{
+  context.push_target();
+  context.set_target(DrawingContext::LIGHTMAP);
+
+  sprite->set_color(color);
+  sprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+  sprite->set_angle(90); // FIXME: color won't get applied for angle=0
+  sprite->draw(context, position, 0);
+
+  context.pop_target();
+}
diff --git a/src/object/light.hpp b/src/object/light.hpp
new file mode 100644 (file)
index 0000000..b9b625b
--- /dev/null
@@ -0,0 +1,45 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LIGHT_HPP__
+#define __LIGHT_HPP__
+
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+class Light : public GameObject
+{
+public:
+  Light(const Vector& center, const Color& color = Color(1.0, 1.0, 1.0, 1.0));
+  virtual ~Light();
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+protected:
+  Vector position;
+  Color color;
+  Sprite* sprite;
+};
+
+#endif
diff --git a/src/object/magicblock.cpp b/src/object/magicblock.cpp
new file mode 100644 (file)
index 0000000..734186f
--- /dev/null
@@ -0,0 +1,159 @@
+//  $Id$
+//
+//  SuperTux - MagicBlock
+//
+//  Magic Blocks are tile-like game objects that are sensitive to
+//  lighting conditions. They are rendered in a color and
+//  will only be solid as long as light of the same color shines
+//  on the block.
+//
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <vector>
+
+#include "object/camera.hpp"
+#include "magicblock.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "main.hpp"
+
+namespace {
+  const float MIN_INTENSITY = 0.8f;
+  const float ALPHA_SOLID = 0.7f;
+  const float ALPHA_NONSOLID = 0.3f;
+  const float MIN_SOLIDTIME = 1.0f;
+  const float SWITCH_DELAY = 0.1f; /**< seconds to wait for stable conditions until switching solidity */
+}
+
+MagicBlock::MagicBlock(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/magicblock/magicblock.sprite"),
+        is_solid(false), solid_time(0), switch_delay(0), light(1.0f,1.0f,1.0f)
+{
+  set_group(COLGROUP_STATIC);
+  //get color from lisp
+  std::vector<float> vColor;
+  lisp.get_vector("color", vColor );
+  color = Color( vColor );
+
+  //all alpha to make the sprite still visible
+  color.alpha = ALPHA_SOLID;
+
+  //set trigger
+  if(color.red == 0 && color.green == 0 && color.blue == 0) { //is it black?
+    black = true;
+    trigger_red = MIN_INTENSITY;
+    trigger_green = MIN_INTENSITY;
+    trigger_blue = MIN_INTENSITY;
+  } else {
+    black = false;
+    trigger_red = (color.red == 1.0f ? MIN_INTENSITY : 0);
+    trigger_green = (color.green == 1.0f ? MIN_INTENSITY : 0);
+    trigger_blue = (color.blue == 1.0f ? MIN_INTENSITY : 0);
+  }
+
+  center = get_bbox().get_middle();
+}
+
+void
+MagicBlock::update(float elapsed_time)
+{
+  //Check if center of this block is on screen.
+  //Don't update if not, because there is no light off screen.
+  float screen_left = Sector::current()->camera->get_translation().x;
+  float screen_top = Sector::current()->camera->get_translation().y;
+  float screen_right = screen_left+ SCREEN_WIDTH;
+  float screen_bottom = screen_top + SCREEN_HEIGHT;
+  if((center.x > screen_right ) || ( center.y > screen_bottom) ||
+     ( center.x < screen_left) || ( center.y < screen_top)) {
+    switch_delay = SWITCH_DELAY;
+    return;
+  }
+
+  bool lighting_ok;
+  if(black) {
+    lighting_ok = (light.red >= trigger_red || light.green >= trigger_green
+      || light.blue >= trigger_blue);
+  }else{
+    lighting_ok = (light.red >= trigger_red && light.green >= trigger_green
+      && light.blue >= trigger_blue);
+  }
+
+  // overrule lighting_ok if switch_delay has not yet passed
+  if (lighting_ok == is_solid) {
+    switch_delay = SWITCH_DELAY;
+  } else {
+    if (switch_delay > 0) {
+      lighting_ok = is_solid;
+      switch_delay -= elapsed_time;
+    }
+  }
+
+  if (lighting_ok) {
+    // lighting suggests going solid
+
+    if (!is_solid) {
+      if (Sector::current()->is_free_of_movingstatics(get_bbox(), this)) {
+        is_solid = true;
+        solid_time = 0;
+        switch_delay = SWITCH_DELAY;
+      }
+    }
+  } else {
+    // lighting suggests going nonsolid
+
+    if( solid_time >= MIN_SOLIDTIME ){
+      is_solid = false;
+    }
+  }
+
+  //Update Sprite.
+  if(is_solid) {
+    solid_time+=elapsed_time;
+    color.alpha = ALPHA_SOLID;
+    sprite->set_action("solid");
+  } else {
+    color.alpha = ALPHA_NONSOLID;
+    sprite->set_action("normal");
+  }
+}
+
+void
+MagicBlock::draw(DrawingContext& context){
+  //Ask for update about lightmap at center of this block
+  context.get_light( center, &light );
+
+  //Draw the Sprite.
+  MovingSprite::draw(context);
+  //Add the color.
+  context.draw_filled_rect( get_bbox(), color, layer);
+}
+
+bool
+MagicBlock::collides(GameObject& /*other*/, const CollisionHit& /*hit*/)
+{
+  return is_solid;
+}
+
+HitResponse
+MagicBlock::collision(GameObject& /*other*/, const CollisionHit& /*hit*/)
+{
+  return SOLID;
+}
+
+IMPLEMENT_FACTORY(MagicBlock, "magicblock");
diff --git a/src/object/magicblock.hpp b/src/object/magicblock.hpp
new file mode 100644 (file)
index 0000000..64d9add
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux - MagicBlock
+//
+//  Magic Blocks are tile-like game objects that are sensitive to
+//  lighting conditions. They are rendered in a color and
+//  will only be solid as long as light of the same color shines
+//  on the block. The black block becomes solid, if any kind of
+//  light is above MIN_INTENSITY.
+//
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_TRAMPOLINE_H
+#define SUPERTUX_TRAMPOLINE_H
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+class MagicBlock: public MovingSprite
+{
+public:
+  MagicBlock(const lisp::Lisp& reader);
+
+  bool collides(GameObject& other, const CollisionHit& hit);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+private:
+  bool is_solid;
+  float trigger_red;
+  float trigger_green;
+  float trigger_blue;
+  float solid_time;
+  float switch_delay; /**< seconds until switching solidity */
+  Color color;
+  Color light;
+  Vector center;
+  bool black;
+};
+
+#endif
diff --git a/src/object/moving_sprite.cpp b/src/object/moving_sprite.cpp
new file mode 100644 (file)
index 0000000..0c746a5
--- /dev/null
@@ -0,0 +1,148 @@
+//  $Id$
+//
+//  SuperTux - MovingSprite Base Class
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stdexcept>
+
+#include "moving_sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "player_status.hpp"
+#include "gameobjs.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+#include "random_generator.hpp"
+#include "audio/sound_source.hpp"
+#include "audio/sound_manager.hpp"
+#include "timer.hpp"
+
+MovingSprite::MovingSprite(const Vector& pos, const std::string& sprite_name, int layer, CollisionGroup collision_group)
+       : sprite_name(sprite_name), layer(layer)
+{
+  bbox.set_pos(pos);
+  sprite = sprite_manager->create(sprite_name);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, const Vector& pos, int layer, CollisionGroup collision_group)
+       : layer(layer)
+{
+  bbox.set_pos(pos);
+  if (!reader.get("sprite", sprite_name))
+    throw std::runtime_error("no sprite name set");
+
+  sprite = sprite_manager->create(sprite_name);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, const std::string& sprite_name, int layer, CollisionGroup collision_group)
+       : sprite_name(sprite_name), layer(layer)
+{
+  if (!reader.get("x", bbox.p1.x))
+    throw std::runtime_error("no x position set");
+  if (!reader.get("y", bbox.p1.y))
+    throw std::runtime_error("no y position set");
+
+  sprite = sprite_manager->create(sprite_name);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, int layer, CollisionGroup collision_group)
+       : layer(layer)
+{
+  if (!reader.get("x", bbox.p1.x))
+    throw std::runtime_error("no x position set");
+  if (!reader.get("y", bbox.p1.y))
+    throw std::runtime_error("no y position set");
+  if (!reader.get("sprite", sprite_name))
+    throw std::runtime_error("no sprite name set");
+
+  sprite = sprite_manager->create(sprite_name);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const MovingSprite& other)
+       : MovingObject(other), layer(other.layer)
+{
+  sprite = new Sprite(*other.sprite);
+}
+
+MovingSprite&
+MovingSprite::operator=(const MovingSprite& other)
+{
+  if (this == &other)
+    return *this;
+
+  delete sprite;
+  sprite = new Sprite(*other.sprite);
+
+  layer = other.layer;
+
+  return *this;
+}
+
+MovingSprite::~MovingSprite()
+{
+  delete sprite;
+}
+
+void
+MovingSprite::draw(DrawingContext& context)
+{
+  sprite->draw(context, get_pos(), layer);
+}
+
+void
+MovingSprite::update(float )
+{
+}
+
+void
+MovingSprite::set_action(const std::string& action, int loops)
+{
+  sprite->set_action(action, loops);
+  set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+void
+MovingSprite::set_action_centered(const std::string& action, int loops)
+{
+  Vector old_size = bbox.get_size();
+  sprite->set_action(action, loops);
+  set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+  set_pos(get_pos() - (bbox.get_size() - old_size) / 2);
+}
+
+void
+MovingSprite::set_action(const std::string& action, int loops, AnchorPoint anchorPoint)
+{
+  Rect old_bbox = bbox;
+  sprite->set_action(action, loops);
+  float w = sprite->get_current_hitbox_width();
+  float h = sprite->get_current_hitbox_height();
+  set_size(w, h);
+  set_pos(get_anchor_pos(old_bbox, w, h, anchorPoint));
+}
diff --git a/src/object/moving_sprite.hpp b/src/object/moving_sprite.hpp
new file mode 100644 (file)
index 0000000..9991474
--- /dev/null
@@ -0,0 +1,70 @@
+//  $Id$
+//
+//  SuperTux - MovingSprite Base Class
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __MOVING_SPRITE_H__
+#define __MOVING_SPRITE_H__
+
+#include <string>
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "object/anchor_point.hpp"
+
+/**
+ * Abstract base class for MovingObjects that are represented by a Sprite
+ */
+class MovingSprite : public MovingObject
+{
+public:
+  MovingSprite(const Vector& pos, const std::string& sprite_name, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+  MovingSprite(const lisp::Lisp& reader, const Vector& pos, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+  MovingSprite(const lisp::Lisp& reader, const std::string& sprite_name, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+  MovingSprite(const lisp::Lisp& reader, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+  MovingSprite(const MovingSprite& moving_sprite);
+  MovingSprite& operator=(const MovingSprite& moving_sprite);
+  ~MovingSprite();
+
+  virtual void draw(DrawingContext& context);
+  virtual void update(float elapsed_time);
+
+protected:
+  std::string sprite_name;
+  Sprite* sprite;
+  int layer; /**< Sprite's z-position. Refer to video/drawing_context.hpp for sensible values. */
+
+  /**
+   * set new action for sprite and resize bounding box.
+   * use with care as you can easily get stuck when resizing the bounding box.
+   */
+  void set_action(const std::string& action, int loops);
+
+  /**
+   * set new action for sprite and re-center bounding box.
+   * use with care as you can easily get stuck when resizing the bounding box.
+   */
+  void set_action_centered(const std::string& action, int loops);
+
+  /**
+   * set new action for sprite and align bounding boxes at anchorPoint.
+   * use with care as you can easily get stuck when resizing the bounding box.
+   */
+  void set_action(const std::string& action, int loops, AnchorPoint anchorPoint);
+};
+
+#endif
diff --git a/src/object/oneup.cpp b/src/object/oneup.cpp
new file mode 100644 (file)
index 0000000..322cb23
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "oneup.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "player_status.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "statistics.hpp"
+#include "video/drawing_context.hpp"
+
+OneUp::OneUp(const Vector& pos, Direction direction)
+       : MovingSprite(pos, "images/powerups/1up/1up.sprite", LAYER_FLOATINGOBJECTS, COLGROUP_TOUCHABLE)
+{
+  physic.set_velocity((direction == LEFT)?-100:100, -400);
+}
+
+void
+OneUp::update(float elapsed_time)
+{
+  if(!Sector::current()->inside(bbox))
+    remove_me();
+
+  movement = physic.get_movement(elapsed_time);
+}
+
+HitResponse
+OneUp::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player) {
+    player->get_status()->add_coins(100);
+#if 0
+    // FIXME: do we want this? q.v. src/level.cpp
+    Sector::current()->get_level()->stats.coins += 100;
+#endif
+    remove_me();
+    return ABORT_MOVE;
+  }
+  return FORCE_MOVE;
+}
diff --git a/src/object/oneup.hpp b/src/object/oneup.hpp
new file mode 100644 (file)
index 0000000..1e23841
--- /dev/null
@@ -0,0 +1,37 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ONEUP_H__
+#define __ONEUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class OneUp : public MovingSprite, private UsesPhysic
+{
+public:
+  OneUp(const Vector& pos, Direction direction = RIGHT);
+  virtual OneUp* clone() const { return new OneUp(*this); }
+
+  virtual void update(float elapsed_time);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
diff --git a/src/object/particles.cpp b/src/object/particles.cpp
new file mode 100644 (file)
index 0000000..f6f066b
--- /dev/null
@@ -0,0 +1,104 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+
+#include "particles.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "random_generator.hpp"
+
+Particles::Particles(const Vector& epicenter, int min_angle, int max_angle,
+        const Vector& initial_velocity, const Vector& acceleration, int number,
+        Color color_, int size_, float life_time, int drawing_layer_)
+  : accel(acceleration), color(color_), size(size_), drawing_layer(drawing_layer_)
+{
+  if(life_time == 0) {
+    live_forever = true;
+  } else {
+    live_forever = false;
+    timer.start(life_time);
+  }
+
+  // create particles
+  for(int p = 0; p < number; p++)
+    {
+    Particle* particle = new Particle;
+    particle->pos = epicenter;
+
+    float angle = systemRandom.rand(min_angle, max_angle)
+                      * (M_PI / 180);  // convert to radius (radians?)
+    particle->vel.x = /*fabs*/(sin(angle)) * initial_velocity.x;
+//    if(angle >= M_PI && angle < M_PI*2)
+//      particle->vel.x *= -1;  // work around to fix signal
+    particle->vel.y = /*fabs*/(cos(angle)) * initial_velocity.y;
+//    if(angle >= M_PI_2 && angle < 3*M_PI_2)
+//      particle->vel.y *= -1;
+
+    particles.push_back(particle);
+    }
+}
+
+Particles::~Particles()
+{
+  // free particles
+  for(std::vector<Particle*>::iterator i = particles.begin();
+      i < particles.end(); i++)
+    delete (*i);
+}
+
+void
+Particles::update(float elapsed_time)
+{
+  Vector camera = Sector::current()->camera->get_translation();
+
+  // update particles
+  for(std::vector<Particle*>::iterator i = particles.begin();
+      i != particles.end(); ) {
+    (*i)->pos.x += (*i)->vel.x * elapsed_time;
+    (*i)->pos.y += (*i)->vel.y * elapsed_time;
+
+    (*i)->vel.x += accel.x * elapsed_time;
+    (*i)->vel.y += accel.y * elapsed_time;
+
+    if((*i)->pos.x < camera.x || (*i)->pos.x > SCREEN_WIDTH + camera.x ||
+       (*i)->pos.y < camera.y || (*i)->pos.y > SCREEN_HEIGHT + camera.y) {
+      delete (*i);
+      i = particles.erase(i);
+    } else {
+      ++i;
+    }
+  }
+
+  if((timer.check() && !live_forever) || particles.size() == 0)
+    remove_me();
+}
+
+void
+Particles::draw(DrawingContext& context)
+{
+  // draw particles
+  for(std::vector<Particle*>::iterator i = particles.begin();
+      i != particles.end(); i++) {
+    context.draw_filled_rect((*i)->pos, Vector(size,size), color,drawing_layer);
+  }
+}
diff --git a/src/object/particles.hpp b/src/object/particles.hpp
new file mode 100644 (file)
index 0000000..9c353ee
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_PARTICLES_HPP
+#define SUPERTUX_PARTICLES_HPP
+
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "video/color.hpp"
+
+class Particles : public GameObject
+{
+public:
+  Particles(const Vector& epicenter, int min_angle, int max_angle,
+            const Vector& initial_velocity, const Vector& acceleration,
+            int number, Color color, int size, float life_time,
+            int drawing_layer);
+  ~Particles();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  Vector accel;
+  Timer timer;
+  bool live_forever;
+
+  Color color;
+  float size;
+  int drawing_layer;
+
+  struct Particle {
+    Vector pos, vel;
+//     float angle;
+    };
+  std::vector <Particle*> particles;
+};
+
+#endif
diff --git a/src/object/particlesystem.cpp b/src/object/particlesystem.cpp
new file mode 100644 (file)
index 0000000..1c446b1
--- /dev/null
@@ -0,0 +1,250 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+#include <cmath>
+
+#include "particlesystem.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "object/camera.hpp"
+#include "random_generator.hpp"
+
+ParticleSystem::ParticleSystem(float max_particle_size)
+       : max_particle_size(max_particle_size)
+{
+  virtual_width = SCREEN_WIDTH + max_particle_size * 2;
+  virtual_height = SCREEN_HEIGHT + max_particle_size *2;
+  z_pos = LAYER_BACKGROUND1;
+}
+
+ParticleSystem::~ParticleSystem()
+{
+  std::vector<Particle*>::iterator i;
+  for(i = particles.begin(); i != particles.end(); ++i) {
+    delete *i;
+  }
+}
+
+void ParticleSystem::draw(DrawingContext& context)
+{
+  float scrollx = context.get_translation().x;
+  float scrolly = context.get_translation().y;
+
+  context.push_transform();
+  context.set_translation(Vector(max_particle_size,max_particle_size));
+
+  std::vector<Particle*>::iterator i;
+  for(i = particles.begin(); i != particles.end(); ++i) {
+    Particle* particle = *i;
+
+    // remap x,y coordinates onto screencoordinates
+    Vector pos;
+
+    pos.x = fmodf(particle->pos.x - scrollx, virtual_width);
+    if(pos.x < 0) pos.x += virtual_width;
+
+    pos.y = fmodf(particle->pos.y - scrolly, virtual_height);
+    if(pos.y < 0) pos.y += virtual_height;
+
+
+    //if(pos.x > virtual_width) pos.x -= virtual_width;
+    //if(pos.y > virtual_height) pos.y -= virtual_height;
+
+    context.draw_surface(particle->texture, pos, z_pos);
+  }
+
+  context.pop_transform();
+}
+
+SnowParticleSystem::SnowParticleSystem()
+{
+  snowimages[0] = new Surface("images/objects/particles/snow2.png");
+  snowimages[1] = new Surface("images/objects/particles/snow1.png");
+  snowimages[2] = new Surface("images/objects/particles/snow0.png");
+
+  virtual_width = SCREEN_WIDTH * 2;
+
+  // create some random snowflakes
+  size_t snowflakecount = size_t(virtual_width/10.0);
+  for(size_t i=0; i<snowflakecount; ++i) {
+    SnowParticle* particle = new SnowParticle;
+    int snowsize = systemRandom.rand(3);
+
+    particle->pos.x = systemRandom.randf(virtual_width);
+    particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+    particle->anchorx = particle->pos.x + (systemRandom.randf(-0.5, 0.5) * 16);
+    particle->drift_speed = systemRandom.randf(-0.5, 0.5) * 0.3;
+    particle->wobble = 0.0;
+
+    particle->texture = snowimages[snowsize];
+
+    particle->speed = 1 + (2 - snowsize)/2 + systemRandom.randf(1.8);
+    particle->speed *= 20; // gravity
+
+    particles.push_back(particle);
+  }
+}
+
+void
+SnowParticleSystem::parse(const lisp::Lisp& reader)
+{
+  reader.get("z-pos", z_pos);
+}
+
+void
+SnowParticleSystem::write(lisp::Writer& writer)
+{
+  writer.start_list("particles-snow");
+  writer.write_int("z-pos", z_pos);
+  writer.end_list("particles-snow");
+}
+
+SnowParticleSystem::~SnowParticleSystem()
+{
+  for(int i=0;i<3;++i)
+    delete snowimages[i];
+}
+
+void SnowParticleSystem::update(float elapsed_time)
+{
+  std::vector<Particle*>::iterator i;
+
+  for(i = particles.begin(); i != particles.end(); ++i) {
+    SnowParticle* particle = (SnowParticle*) *i;
+    float anchor_delta;
+
+    particle->pos.y += particle->speed * elapsed_time;
+    particle->pos.x += particle->wobble * elapsed_time /* * particle->speed * 0.125*/;
+
+    anchor_delta = (particle->anchorx - particle->pos.x);
+    particle->wobble += (4 * anchor_delta * 0.05) + systemRandom.randf(-0.5, 0.5);
+    particle->wobble *= 0.99f;
+    particle->anchorx += particle->drift_speed * elapsed_time;
+  }
+}
+
+//FIXME: Sometimes both ghosts have the same image
+//       Ghosts don't change their movement pattern - not random
+GhostParticleSystem::GhostParticleSystem()
+{
+  ghosts[0] = new Surface("images/objects/particles/ghost0.png");
+  ghosts[1] = new Surface("images/objects/particles/ghost1.png");
+
+  virtual_width = SCREEN_WIDTH * 2;
+
+  // create two ghosts
+  size_t ghostcount = 2;
+  for(size_t i=0; i<ghostcount; ++i) {
+    GhostParticle* particle = new GhostParticle;
+    particle->pos.x = systemRandom.randf(virtual_width);
+    particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+    int size = systemRandom.rand(2);
+    particle->texture = ghosts[size];
+    particle->speed = systemRandom.randf(std::max(50, (size * 10)), 180 + (size * 10));
+    particles.push_back(particle);
+  }
+}
+
+void
+GhostParticleSystem::parse(const lisp::Lisp& reader)
+{
+  reader.get("z-pos", z_pos);
+}
+
+void
+GhostParticleSystem::write(lisp::Writer& writer)
+{
+  writer.start_list("particles-ghosts");
+  writer.write_int("z-pos", z_pos);
+  writer.end_list("particles-ghosts");
+}
+
+GhostParticleSystem::~GhostParticleSystem()
+{
+  for(int i=0;i<2;++i)
+    delete ghosts[i];
+}
+
+void GhostParticleSystem::update(float elapsed_time)
+{
+  std::vector<Particle*>::iterator i;
+  for(i = particles.begin(); i != particles.end(); ++i) {
+    GhostParticle* particle = (GhostParticle*) *i;
+    particle->pos.y -= particle->speed * elapsed_time;
+    particle->pos.x -= particle->speed * elapsed_time;
+    if(particle->pos.y > SCREEN_HEIGHT) {
+      particle->pos.y = fmodf(particle->pos.y , virtual_height);
+      particle->pos.x = systemRandom.rand(static_cast<int>(virtual_width));
+    }
+  }
+}
+
+CloudParticleSystem::CloudParticleSystem()
+       : ParticleSystem(128)
+{
+  cloudimage = new Surface("images/objects/particles/cloud.png");
+
+  virtual_width = 2000.0;
+
+  // create some random clouds
+  for(size_t i=0; i<15; ++i) {
+    CloudParticle* particle = new CloudParticle;
+    particle->pos.x = systemRandom.rand(static_cast<int>(virtual_width));
+    particle->pos.y = systemRandom.rand(static_cast<int>(virtual_height));
+    particle->texture = cloudimage;
+    particle->speed = -systemRandom.randf(25.0, 54.0);
+
+    particles.push_back(particle);
+  }
+}
+
+void
+CloudParticleSystem::parse(const lisp::Lisp& reader)
+{
+  reader.get("z-pos", z_pos);
+}
+
+void
+CloudParticleSystem::write(lisp::Writer& writer)
+{
+  writer.start_list("particles-clouds");
+  writer.write_int("z-pos", z_pos);
+  writer.end_list("particles-clouds");
+}
+
+CloudParticleSystem::~CloudParticleSystem()
+{
+  delete cloudimage;
+}
+
+void CloudParticleSystem::update(float elapsed_time)
+{
+  std::vector<Particle*>::iterator i;
+  for(i = particles.begin(); i != particles.end(); ++i) {
+    CloudParticle* particle = (CloudParticle*) *i;
+    particle->pos.x += particle->speed * elapsed_time;
+  }
+}
diff --git a/src/object/particlesystem.hpp b/src/object/particlesystem.hpp
new file mode 100644 (file)
index 0000000..b3f49a5
--- /dev/null
@@ -0,0 +1,153 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_PARTICLESYSTEM_H
+#define SUPERTUX_PARTICLESYSTEM_H
+
+#include <vector>
+
+#include "video/surface.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "sector.hpp"
+#include "math/vector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class DisplayManager;
+
+/**
+ * This is the base class for particle systems. It is responsible for storing a
+ * set of particles with each having an x- and y-coordinate the number of the
+ * layer where it should be drawn and a texture.
+ * The coordinate system used here is a virtual one. It would be a bad idea to
+ * populate whole levels with particles. So we're using a virtual rectangle
+ * here that is tiled onto the level when drawing. This rect.has the size
+ * (virtual_width, virtual_height). We're using modulo on the particle
+ * coordinates, so when a particle leaves left, it'll reenter at the right
+ * side.
+ *
+ * Classes that implement a particle system should subclass from this class,
+ * initialize particles in the constructor and move them in the simulate
+ * function.
+ */
+class ParticleSystem : public GameObject
+{
+public:
+    ParticleSystem(float max_particle_size = 60);
+    virtual ~ParticleSystem();
+
+    virtual void draw(DrawingContext& context);
+
+protected:
+    float max_particle_size;
+    int z_pos;
+
+    class Particle
+    {
+    public:
+        virtual ~Particle()
+        { }
+
+        Vector pos;
+        Surface* texture;
+    };
+
+    std::vector<Particle*> particles;
+    float virtual_width, virtual_height;
+};
+
+class SnowParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+    SnowParticleSystem();
+    virtual ~SnowParticleSystem();
+
+    void parse(const lisp::Lisp& lisp);
+    void write(lisp::Writer& writer);
+
+    virtual void update(float elapsed_time);
+
+    std::string type() const
+    { return "SnowParticleSystem"; }
+
+private:
+    class SnowParticle : public Particle
+    {
+    public:
+        float speed;
+       float wobble;
+       float anchorx;
+       float drift_speed;
+    };
+
+    Surface* snowimages[3];
+};
+
+class GhostParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+    GhostParticleSystem();
+    virtual ~GhostParticleSystem();
+
+    void parse(const lisp::Lisp& lisp);
+    void write(lisp::Writer& writer);
+
+    virtual void update(float elapsed_time);
+
+    std::string type() const
+    { return "GhostParticleSystem"; }
+
+private:
+    class GhostParticle : public Particle
+    {
+    public:
+        float speed;
+    };
+
+    Surface* ghosts[2];
+};
+
+class CloudParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+    CloudParticleSystem();
+    virtual ~CloudParticleSystem();
+
+    void parse(const lisp::Lisp& lisp);
+    void write(lisp::Writer& writer);
+
+    virtual void update(float elapsed_time);
+
+    std::string type() const
+    { return "CloudParticleSystem"; }
+
+private:
+    class CloudParticle : public Particle
+    {
+    public:
+        float speed;
+    };
+
+    Surface* cloudimage;
+};
+
+#endif
diff --git a/src/object/particlesystem_interactive.cpp b/src/object/particlesystem_interactive.cpp
new file mode 100644 (file)
index 0000000..2d4a3c2
--- /dev/null
@@ -0,0 +1,304 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+#include <cmath>
+
+#include "particlesystem_interactive.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+#include "tile.hpp"
+#include "tilemap.hpp"
+#include "math/aatriangle.hpp"
+#include "collision.hpp"
+#include "collision_hit.hpp"
+#include "object/camera.hpp"
+#include "object/rainsplash.hpp"
+#include "badguy/bomb.hpp"
+#include "random_generator.hpp"
+
+//TODO: Find a way to make rain collide with objects like bonus blocks
+//      Add an option to set rain strength
+//      Fix rain being "respawned" over solid tiles
+ParticleSystem_Interactive::ParticleSystem_Interactive()
+{
+    virtual_width = SCREEN_WIDTH;
+    virtual_height = SCREEN_HEIGHT;
+    z_pos = 0;
+}
+
+ParticleSystem_Interactive::~ParticleSystem_Interactive()
+{
+    std::vector<Particle*>::iterator i;
+    for(i = particles.begin(); i != particles.end(); ++i) {
+        delete *i;
+    }
+}
+
+void ParticleSystem_Interactive::draw(DrawingContext& context)
+{
+  context.push_transform();
+
+    std::vector<Particle*>::iterator i;
+    for(i = particles.begin(); i != particles.end(); ++i) {
+        Particle* particle = *i;
+        context.draw_surface(particle->texture, particle->pos, z_pos);
+    }
+
+    context.pop_transform();
+}
+
+int
+ParticleSystem_Interactive::collision(Particle* object, Vector movement)
+{
+  using namespace collision;
+
+  // calculate rectangle where the object will move
+  float x1, x2;
+  float y1, y2;
+  x1 = object->pos.x;
+  x2 = x1 + 32 + movement.x;
+  y1 = object->pos.y;
+  y2 = y1 + 32 + movement.y;
+  bool water = false;
+
+  // test with all tiles in this rectangle
+  int starttilex = int(x1-1) / 32;
+  int starttiley = int(y1-1) / 32;
+  int max_x = int(x2+1);
+  int max_y = int(y2+1);
+
+  Rect dest = Rect(x1, y1, x2, y2);
+  dest.move(movement);
+  Constraints constraints;
+
+  for(std::list<TileMap*>::const_iterator i = Sector::current()->solid_tilemaps.begin(); i != Sector::current()->solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    for(int x = starttilex; x*32 < max_x; ++x) {
+      for(int y = starttiley; y*32 < max_y; ++y) {
+       const Tile* tile = solids->get_tile(x, y);
+       if(!tile)
+         continue;
+       // skip non-solid tiles, except water
+       if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
+         continue;
+
+       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
+         AATriangle triangle;
+         Vector p1(x*32, y*32);
+         Vector p2((x+1)*32, (y+1)*32);
+         triangle = AATriangle(p1, p2, tile->getData());
+
+         if(rectangle_aatriangle(&constraints, dest, triangle)) {
+           if(tile->getAttributes() & Tile::WATER)
+             water = true;
+         }
+       } else { // normal rectangular tile
+         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
+         if(intersects(dest, rect)) {
+           if(tile->getAttributes() & Tile::WATER)
+             water = true;
+           set_rectangle_rectangle_constraints(&constraints, dest, rect);
+         }
+       }
+      }
+    }
+  }
+
+
+  // TODO don't use magic numbers here...
+
+  // did we collide at all?
+  if(!constraints.has_constraints())
+    return -1;
+
+  const CollisionHit& hit = constraints.hit;
+  if (water) {
+    return 0; //collision with water tile - don't draw splash
+  } else {
+    if (hit.right || hit.left) {
+      return 2; //collision from right
+    } else {
+      return 1; //collision from above
+    }
+  }
+
+  return 0;
+}
+
+RainParticleSystem::RainParticleSystem()
+{
+    rainimages[0] = new Surface("images/objects/particles/rain0.png");
+    rainimages[1] = new Surface("images/objects/particles/rain1.png");
+
+    virtual_width = SCREEN_WIDTH * 2;
+
+    // create some random raindrops
+    size_t raindropcount = size_t(virtual_width/6.0);
+    for(size_t i=0; i<raindropcount; ++i) {
+        RainParticle* particle = new RainParticle;
+        particle->pos.x = systemRandom.rand(int(virtual_width));
+        particle->pos.y = systemRandom.rand(int(virtual_height));
+        int rainsize = systemRandom.rand(2);
+        particle->texture = rainimages[rainsize];
+        do {
+            particle->speed = (rainsize+1)*45 + systemRandom.randf(3.6);
+        } while(particle->speed < 1);
+        particle->speed *= 10; // gravity
+
+        particles.push_back(particle);
+    }
+}
+
+void
+RainParticleSystem::parse(const lisp::Lisp& reader)
+{
+  reader.get("z-pos", z_pos);
+}
+
+void
+RainParticleSystem::write(lisp::Writer& writer)
+{
+  writer.start_list("particles-rain");
+  writer.write_int("z-pos", z_pos);
+  writer.end_list("particles-rain");
+}
+
+RainParticleSystem::~RainParticleSystem()
+{
+  for(int i=0;i<2;++i)
+    delete rainimages[i];
+}
+
+void RainParticleSystem::update(float elapsed_time)
+{
+    std::vector<Particle*>::iterator i;
+    for(
+        i = particles.begin(); i != particles.end(); ++i) {
+        RainParticle* particle = (RainParticle*) *i;
+        float movement = particle->speed * elapsed_time;
+        float abs_x = Sector::current()->camera->get_translation().x;
+        float abs_y = Sector::current()->camera->get_translation().y;
+        particle->pos.y += movement;
+        particle->pos.x -= movement;
+        int col = collision(particle, Vector(-movement, movement));
+        if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
+            //Create rainsplash
+            if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)){
+              bool vertical = (col == 2);
+              int splash_x, splash_y;
+              if (!vertical) { //check if collision happened from above
+                splash_x = int(particle->pos.x);
+                splash_y = int(particle->pos.y) - (int(particle->pos.y) % 32) + 32;
+                Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
+              }
+              // Uncomment the following to display vertical splashes, too
+              /* else {
+                splash_x = int(particle->pos.x) - (int(particle->pos.x) % 32) + 32;
+                splash_y = int(particle->pos.y);
+                Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
+              } */
+            }
+            int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
+            int new_y = 0;
+            //FIXME: Don't move particles over solid tiles
+            particle->pos.x = new_x;
+            particle->pos.y = new_y;
+        }
+    }
+}
+
+CometParticleSystem::CometParticleSystem()
+{
+    cometimages[0] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
+    cometimages[1] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
+
+    virtual_width = SCREEN_WIDTH * 2;
+
+    // create some random comets
+    size_t cometcount = 2;
+    for(size_t i=0; i<cometcount; ++i) {
+        CometParticle* particle = new CometParticle;
+        particle->pos.x = systemRandom.rand(int(virtual_width));
+        particle->pos.y = systemRandom.rand(int(virtual_height));
+        int cometsize = systemRandom.rand(2);
+        particle->texture = cometimages[cometsize];
+        do {
+            particle->speed = (cometsize+1)*30 + systemRandom.randf(3.6);
+        } while(particle->speed < 1);
+        particle->speed *= 10; // gravity
+
+        particles.push_back(particle);
+    }
+}
+
+void
+CometParticleSystem::parse(const lisp::Lisp& reader)
+{
+  reader.get("z-pos", z_pos);
+}
+
+void
+CometParticleSystem::write(lisp::Writer& writer)
+{
+  writer.start_list("particles-comets");
+  writer.write_int("z-pos", z_pos);
+  writer.end_list("particles-comets");
+}
+
+CometParticleSystem::~CometParticleSystem()
+{
+  for(int i=0;i<2;++i)
+    delete cometimages[i];
+}
+
+void CometParticleSystem::update(float elapsed_time)
+{
+  (void) elapsed_time;
+#if 0
+    std::vector<Particle*>::iterator i;
+    for(
+        i = particles.begin(); i != particles.end(); ++i) {
+        CometParticle* particle = (CometParticle*) *i;
+        float movement = particle->speed * elapsed_time;
+        float abs_x = Sector::current()->camera->get_translation().x;
+        float abs_y = Sector::current()->camera->get_translation().y;
+        particle->pos.y += movement;
+        particle->pos.x -= movement;
+        int col = collision(particle, Vector(-movement, movement));
+        if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
+            if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)) {
+              Sector::current()->add_object(new Bomb(particle->pos, LEFT));
+            }
+            int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
+            int new_y = 0;
+            //FIXME: Don't move particles over solid tiles
+            particle->pos.x = new_x;
+            particle->pos.y = new_y;
+        }
+    }
+#endif
+}
diff --git a/src/object/particlesystem_interactive.hpp b/src/object/particlesystem_interactive.hpp
new file mode 100644 (file)
index 0000000..55bf3c5
--- /dev/null
@@ -0,0 +1,122 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_PARTICLESYSTEM_INTERACTIVE_H
+#define SUPERTUX_PARTICLESYSTEM_INTERACTIVE_H
+
+#include <vector>
+
+#include "video/surface.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "sector.hpp"
+#include "math/vector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class DisplayManager;
+
+/**
+ * This is an alternative class for particle systems. It is responsible for storing a
+ * set of particles with each having an x- and y-coordinate the number of the
+ * layer where it should be drawn and a texture.
+ * This version of the particle system class doesn't use virtual screen coordinates,
+ * but Interactive ones. Particle systems which need Interactive levels coordinates, such
+ * as rain, should be implemented here.
+ * Classes that implement a particle system should subclass from this class,
+ * initialize particles in the constructor and move them in the simulate
+ * function.
+ */
+class ParticleSystem_Interactive : public GameObject
+{
+public:
+    ParticleSystem_Interactive();
+    virtual ~ParticleSystem_Interactive();
+
+    virtual void draw(DrawingContext& context);
+
+protected:
+    int z_pos;
+
+    class Particle
+    {
+    public:
+        virtual ~Particle()
+        { }
+
+        Vector pos;
+        Surface* texture;
+    };
+
+    std::vector<Particle*> particles;
+    float virtual_width, virtual_height;
+    int collision(Particle* particle, Vector movement);
+};
+
+class RainParticleSystem : public ParticleSystem_Interactive, public Serializable
+{
+public:
+    RainParticleSystem();
+    virtual ~RainParticleSystem();
+
+    void parse(const lisp::Lisp& lisp);
+    void write(lisp::Writer& writer);
+
+    virtual void update(float elapsed_time);
+
+    std::string type() const
+    { return "RainParticleSystem"; }
+
+private:
+    class RainParticle : public Particle
+    {
+    public:
+        float speed;
+    };
+
+    Surface* rainimages[2];
+};
+
+class CometParticleSystem : public ParticleSystem_Interactive, public Serializable
+{
+public:
+    CometParticleSystem();
+    virtual ~CometParticleSystem();
+
+    void parse(const lisp::Lisp& lisp);
+    void write(lisp::Writer& writer);
+
+    virtual void update(float elapsed_time);
+
+    std::string type() const
+    { return "CometParticleSystem"; }
+
+private:
+    class CometParticle : public Particle
+    {
+    public:
+        float speed;
+    };
+
+    Surface* cometimages[2];
+};
+
+#endif
diff --git a/src/object/path.cpp b/src/object/path.cpp
new file mode 100644 (file)
index 0000000..5d7e88a
--- /dev/null
@@ -0,0 +1,169 @@
+//  $Id$
+//
+//  SuperTux Path
+//  Copyright (C) 2005 Philipp <balinor@pnxs.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "path.hpp"
+
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "object_factory.hpp"
+#include "log.hpp"
+
+#include <assert.h>
+#include <iostream>
+#include <stdexcept>
+#include <sstream>
+
+Path::Path()
+{
+}
+
+Path::~Path()
+{
+}
+
+void
+Path::read(const lisp::Lisp& reader)
+{
+  lisp::ListIterator iter(&reader);
+
+  mode = CIRCULAR;
+  while(iter.next()) {
+    if(iter.item() == "mode") {
+      std::string mode_string;
+      if(!iter.value()->get(mode_string))
+        throw std::runtime_error("Pathmode not a string");
+
+      if(mode_string == "oneshot")
+        mode = ONE_SHOT;
+      else if(mode_string == "pingpong")
+        mode = PING_PONG;
+      else if(mode_string == "circular")
+        mode = CIRCULAR;
+      else {
+        std::ostringstream msg;
+        msg << "Unknown pathmode '" << mode_string << "' found";
+        throw std::runtime_error(msg.str());
+      }
+      continue;
+    }
+
+    if(iter.item() != "node") {
+      log_warning << "unknown token '" << iter.item() << "' in Path nodes list. Ignored." << std::endl;
+      continue;
+    }
+    const lisp::Lisp* node_lisp = iter.lisp();
+
+    // each new node will inherit all values from the last one
+    Node node;
+    node.time = 1;
+    if( (!node_lisp->get("x", node.position.x) ||
+          !node_lisp->get("y", node.position.y)))
+      throw std::runtime_error("Path node without x and y coordinate specified");
+    node_lisp->get("time", node.time);
+
+    if(node.time <= 0)
+      throw std::runtime_error("Path node with non-positive time");
+
+    nodes.push_back(node);
+  }
+
+  if (nodes.empty())
+    throw std::runtime_error("Path with zero nodes");
+}
+
+void
+Path::write(lisp::Writer& writer)
+{
+  writer.start_list("path");
+
+  switch(mode) {
+    case ONE_SHOT:
+      writer.write_string("mode", "oneshot");
+      break;
+    case PING_PONG:
+      writer.write_string("mode", "pingpong");
+      break;
+    case CIRCULAR:
+      writer.write_string("mode", "circular");
+      break;
+    default:
+      log_warning << "Don't know how to write mode " << (int) mode << " ?!?" << std::endl;
+      break;
+  }
+
+  for (size_t i=0; i < nodes.size(); i++) {
+    const Node& node = nodes[i];
+
+    writer.start_list("node");
+    writer.write_float("x", node.position.x);
+    writer.write_float("y", node.position.y);
+    writer.write_float("time", node.time);
+
+    writer.end_list("node");
+  }
+
+  writer.end_list("path");
+}
+
+Vector
+Path::get_base() const
+{
+  if(nodes.empty())
+    return Vector(0, 0);
+
+  return nodes[0].position;
+}
+
+int
+Path::get_nearest_node_no(Vector reference_point) const
+{
+  int nearest_node_id = -1;
+  float nearest_node_dist = 0;
+  int id = 0;
+  for (std::vector<Node>::const_iterator i = nodes.begin(); i != nodes.end(); i++, id++) {
+    float dist = (i->position - reference_point).norm();
+    if ((nearest_node_id == -1) || (dist < nearest_node_dist)) {
+      nearest_node_id = id;
+      nearest_node_dist = dist;
+    }
+  }
+  return nearest_node_id;
+}
+
+int
+Path::get_farthest_node_no(Vector reference_point) const
+{
+  int farthest_node_id = -1;
+  float farthest_node_dist = 0;
+  int id = 0;
+  for (std::vector<Node>::const_iterator i = nodes.begin(); i != nodes.end(); i++, id++) {
+    float dist = (i->position - reference_point).norm();
+    if ((farthest_node_id == -1) || (dist > farthest_node_dist)) {
+      farthest_node_id = id;
+      farthest_node_dist = dist;
+    }
+  }
+  return farthest_node_id;
+}
+
diff --git a/src/object/path.hpp b/src/object/path.hpp
new file mode 100644 (file)
index 0000000..ff17c4e
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux Path
+//  Copyright (C) 2005 Philipp <balinor@pnxs.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __PATH_HPP__
+#define __PATH_HPP__
+
+#include <vector>
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "serializable.hpp"
+
+class Path : public Serializable
+{
+public:
+  Path();
+  ~Path();
+
+  void read(const lisp::Lisp& reader);
+  void write(lisp::Writer& writer);
+
+  Vector get_base() const;
+
+  /**
+   * Helper class that stores an individual node of a Path
+   */
+  class Node
+  {
+  public:
+    Vector position; /**< the position of this node */
+    float time; /**< time (in seconds) to get from this node to next node */
+  };
+
+  std::vector<Node> nodes;
+
+  /**
+   * returns Node index nearest to reference_point or -1 if not applicable
+   */
+  int get_nearest_node_no(Vector reference_point) const;
+
+  /**
+   * returns Node index farthest from reference_point or -1 if not applicable
+   */
+  int get_farthest_node_no(Vector reference_point) const;
+
+private:
+  friend class PathWalker;
+
+  enum WalkMode {
+    // moves from first to last path node and stops
+    ONE_SHOT,
+    // moves from first to last node then in reverse order back to first
+    PING_PONG,
+    // moves from last node back to the first node
+    CIRCULAR
+  };
+
+  WalkMode mode;
+};
+
+#endif
diff --git a/src/object/path_walker.cpp b/src/object/path_walker.cpp
new file mode 100644 (file)
index 0000000..0c8ae25
--- /dev/null
@@ -0,0 +1,152 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include "path_walker.hpp"
+
+PathWalker::PathWalker(const Path* path, bool running)
+  : path(path), running(running), current_node_nr(0), next_node_nr(0), stop_at_node_nr(running?-1:0), node_time(0),
+    walking_speed(1.0)
+{
+  node_mult = 1 / path->nodes[0].time;
+  next_node_nr = path->nodes.size() > 1 ? 1 : 0;
+}
+
+PathWalker::~PathWalker()
+{
+}
+
+Vector
+PathWalker::advance(float elapsed_time)
+{
+  if (!running) return path->nodes[current_node_nr].position;
+
+  assert(elapsed_time >= 0);
+
+  elapsed_time *= fabsf(walking_speed);
+
+  const Path::Node* current_node = & (path->nodes[current_node_nr]);
+  while(node_time + elapsed_time * node_mult >= 1) {
+    elapsed_time -= (1 - node_time) / node_mult;
+
+    if(walking_speed > 0) {
+      advance_node();
+    } else if(walking_speed < 0) {
+      goback_node();
+    }
+
+    current_node = & (path->nodes[current_node_nr]);
+    node_time = 0;
+    if(walking_speed > 0) {
+      node_mult = 1 / current_node->time;
+    } else {
+      node_mult = 1 / path->nodes[next_node_nr].time;
+    }
+  }
+
+  const Path::Node* next_node = & (path->nodes[next_node_nr]);
+  node_time += elapsed_time * node_mult;
+
+  Vector new_pos = current_node->position +
+    (next_node->position - current_node->position) * node_time;
+
+  return new_pos;
+}
+
+void
+PathWalker::goto_node(int node_no)
+{
+  if (node_no == stop_at_node_nr) return;
+  running = true;
+  stop_at_node_nr = node_no;
+}
+
+void
+PathWalker::start_moving()
+{
+  running = true;
+  stop_at_node_nr = -1;
+}
+
+void
+PathWalker::stop_moving()
+{
+  stop_at_node_nr = next_node_nr;
+}
+
+
+void
+PathWalker::advance_node()
+{
+  current_node_nr = next_node_nr;
+  if (static_cast<int>(current_node_nr) == stop_at_node_nr) running = false;
+
+  if(next_node_nr + 1 < path->nodes.size()) {
+    next_node_nr++;
+    return;
+  }
+
+  switch(path->mode) {
+    case Path::ONE_SHOT:
+      next_node_nr = path->nodes.size() - 1;
+      walking_speed = 0;
+      return;
+
+    case Path::PING_PONG:
+      walking_speed = -walking_speed;
+      next_node_nr = path->nodes.size() > 1 ? path->nodes.size() - 2 : 0;
+      return;
+
+    case Path::CIRCULAR:
+      next_node_nr = 0;
+      return;
+  }
+
+  // we shouldn't get here
+  assert(false);
+  next_node_nr = path->nodes.size() - 1;
+  walking_speed = 0;
+}
+
+void
+PathWalker::goback_node()
+{
+  current_node_nr = next_node_nr;
+
+  if(next_node_nr > 0) {
+    next_node_nr--;
+    return;
+  }
+
+  switch(path->mode) {
+    case Path::PING_PONG:
+      walking_speed = -walking_speed;
+      next_node_nr = path->nodes.size() > 1 ? 1 : 0;
+      return;
+    default:
+      break;
+  }
+
+  assert(false);
+  next_node_nr = 0;
+  walking_speed = 0;
+}
diff --git a/src/object/path_walker.hpp b/src/object/path_walker.hpp
new file mode 100644 (file)
index 0000000..829eca1
--- /dev/null
@@ -0,0 +1,87 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PATH_WALKER_HPP__
+#define __PATH_WALKER_HPP__
+
+#include "path.hpp"
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "serializable.hpp"
+
+/**
+ * A walker that travels along a path
+ */
+class PathWalker
+{
+public:
+  PathWalker(const Path* path, bool running = true);
+  virtual ~PathWalker();
+
+  /**
+   * advanves the path walker on the path and returns the position delta
+   * to the last position
+   */
+  virtual Vector advance(float elapsed_time);
+
+  /** advance until at given node, then stop */
+  void goto_node(int node_no);
+
+  /** start advancing automatically */
+  void start_moving();
+
+  /** stop advancing automatically */
+  void stop_moving();
+
+  /** returns true if PathWalker is currently moving */
+  bool is_moving() {
+    return running;
+  }
+  
+  const Path* path;
+
+private:
+  void advance_node();
+  void goback_node();
+
+  /**
+   * set to false to immediately stop advancing
+   */
+  bool running;
+
+  size_t current_node_nr;
+  size_t next_node_nr;
+
+  /**
+   * stop advancing automatically when this node is reached
+   */
+  int stop_at_node_nr;
+
+  /**
+   * the position between the current node and the next node as fraction
+   * between 0 and 1
+   */
+  float node_time;
+  float node_mult;
+
+  float walking_speed;
+};
+
+#endif
diff --git a/src/object/platform.cpp b/src/object/platform.cpp
new file mode 100644 (file)
index 0000000..c6a2da6
--- /dev/null
@@ -0,0 +1,150 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "platform.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "scripting/platform.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "sector.hpp"
+
+Platform::Platform(const lisp::Lisp& reader)
+       : MovingSprite(reader, Vector(0,0), LAYER_OBJECTS, COLGROUP_STATIC), 
+       speed(Vector(0,0)), 
+       automatic(false), player_contact(false), last_player_contact(false)
+{
+  bool running = true;
+  reader.get("name", name);
+  reader.get("running", running);
+  if ((name == "") && (!running)) automatic=true;
+  const lisp::Lisp* pathLisp = reader.get_lisp("path");
+  if(pathLisp == NULL)
+    throw std::runtime_error("No path specified for platform");
+  path.reset(new Path());
+  path->read(*pathLisp);
+  walker.reset(new PathWalker(path.get(), running));
+  bbox.set_pos(path->get_base());
+}
+
+Platform::Platform(const Platform& other)
+       : MovingSprite(other), ScriptInterface(other), 
+       speed(other.speed), 
+       automatic(other.automatic), player_contact(false), last_player_contact(false)
+{
+  name = other.name;
+  path.reset(new Path(*other.path));
+  walker.reset(new PathWalker(*other.walker));
+  walker->path = &*path;
+}
+
+HitResponse
+Platform::collision(GameObject& other, const CollisionHit& )
+{
+  if (dynamic_cast<Player*>(&other)) player_contact = true;
+  return FORCE_MOVE;
+}
+
+void
+Platform::update(float elapsed_time)
+{
+  // check if Platform should automatically pick a destination
+  if (automatic) {
+
+    if (!player_contact && !walker->is_moving()) {
+      // Player doesn't touch platform and Platform is not moving
+
+      // Travel to node nearest to nearest player
+      // FIXME: does not really use nearest player
+      Player* player = 0;      
+      std::vector<Player*> players = Sector::current()->get_players();
+      for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+       player = *playerIter;
+      }
+      if (player) {
+       int nearest_node_id = path->get_nearest_node_no(player->get_bbox().p2);
+       if (nearest_node_id != -1) {
+         goto_node(nearest_node_id);
+       }
+      }
+    } 
+
+    if (player_contact && !last_player_contact && !walker->is_moving()) {
+      // Player touched platform, didn't touch last frame and Platform is not moving
+
+      // Travel to node farthest from current position
+      int farthest_node_id = path->get_farthest_node_no(get_pos());
+      if (farthest_node_id != -1) {
+       goto_node(farthest_node_id);
+      } 
+    }
+
+    // Clear player_contact flag set by collision() method
+    last_player_contact = player_contact;
+    player_contact = false;
+  }
+
+  movement = walker->advance(elapsed_time) - get_pos();
+  speed = movement / elapsed_time;
+}
+
+void
+Platform::goto_node(int node_no)
+{
+  walker->goto_node(node_no);
+}
+
+void
+Platform::start_moving()
+{
+  walker->start_moving();
+}
+
+void
+Platform::stop_moving()
+{
+  walker->stop_moving();
+}
+
+void
+Platform::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::Platform* interface = new Scripting::Platform(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Platform::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+IMPLEMENT_FACTORY(Platform, "platform");
diff --git a/src/object/platform.hpp b/src/object/platform.hpp
new file mode 100644 (file)
index 0000000..06456a7
--- /dev/null
@@ -0,0 +1,85 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+
+/**
+ * This class is the base class for platforms that tux can stand on
+ */
+class Platform : public MovingSprite, public ScriptInterface
+{
+public:
+  Platform(const lisp::Lisp& reader);
+  Platform(const Platform& platform);
+  virtual Platform* clone() const { return new Platform(*this); }
+
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+  virtual void update(float elapsed_time);
+
+  const Vector& get_speed() const
+  {
+    return speed;
+  }
+
+  /**
+   * @name Scriptable Methods
+   * @{
+   */
+
+  /** Move platform until at given node, then stop */
+  void goto_node(int node_no);
+
+  /** Start moving platform */
+  void start_moving();
+
+  /** Stop platform at next node */
+  void stop_moving();
+
+  /**
+   * @}
+   */
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  Path& get_path() {
+    return *path.get();
+  }
+
+private:
+  std::auto_ptr<Path> path;
+  std::auto_ptr<PathWalker> walker;
+
+  Vector speed;
+
+  bool automatic; /**< true if Platform will automatically pick a destination based on collisions and current Player position */
+  bool player_contact; /**< true if a Player touched the Platform during the last round of collision detections */
+  bool last_player_contact; /**< true if a Player touched the Platform during the round before the last round of collision detections */
+
+};
+
+#endif
diff --git a/src/object/player.cpp b/src/object/player.cpp
new file mode 100644 (file)
index 0000000..5b965a2
--- /dev/null
@@ -0,0 +1,1385 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <typeinfo>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+#include "gettext.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+#include "player.hpp"
+#include "tile.hpp"
+#include "sprite/sprite.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "game_session.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/particles.hpp"
+#include "object/portable.hpp"
+#include "object/bullet.hpp"
+#include "trigger/trigger_base.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+#include "platform.hpp"
+#include "badguy/badguy.hpp"
+#include "player_status.hpp"
+#include "log.hpp"
+#include "falling_coin.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+#include "trigger/climbable.hpp"
+
+//#define SWIMMING
+
+static const int TILES_FOR_BUTTJUMP = 3;
+static const float SHOOTING_TIME = .150f;
+/// time before idle animation starts
+static const float IDLE_TIME = 2.5f;
+
+/** acceleration in horizontal direction when walking
+ * (all acceleratiosn are in  pixel/s^2) */
+static const float WALK_ACCELERATION_X = 300;
+/** acceleration in horizontal direction when running */ 
+static const float RUN_ACCELERATION_X = 400;
+/** acceleration when skidding */
+static const float SKID_XM = 200;
+/** time of skidding in seconds */
+static const float SKID_TIME = .3f;
+/** maximum walk velocity (pixel/s) */
+static const float MAX_WALK_XM = 230;
+/** maximum run velcoity (pixel/s) */
+static const float MAX_RUN_XM = 320;
+/** maximum horizontal climb velocity */
+static const float MAX_CLIMB_XM = 48;
+/** maximum vertical climb velocity */
+static const float MAX_CLIMB_YM = 128;
+/** instant velocity when tux starts to walk */
+static const float WALK_SPEED = 100;
+
+/** time of the kick (kicking mriceblock) animation */
+static const float KICK_TIME = .3f;
+/** time of tux cheering (currently unused) */
+static const float CHEER_TIME = 1.0f;
+
+/** if Tux cannot unduck for this long, he will get hurt */
+static const float UNDUCK_HURT_TIME = 0.25f;
+
+// growing animation
+Surface* growingtux_left[GROWING_FRAMES];
+Surface* growingtux_right[GROWING_FRAMES];
+
+Surface* tux_life = 0;
+
+TuxBodyParts* small_tux = 0;
+TuxBodyParts* big_tux = 0;
+TuxBodyParts* fire_tux = 0;
+TuxBodyParts* ice_tux = 0;
+
+namespace{
+  bool no_water = true;
+}
+void
+TuxBodyParts::set_action(std::string action, int loops)
+{
+  if(head != NULL)
+    head->set_action(action, loops);
+  if(body != NULL)
+    body->set_action(action, loops);
+  if(arms != NULL)
+    arms->set_action(action, loops);
+  if(feet != NULL)
+    feet->set_action(action, loops);
+}
+
+void
+TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer, Portable* grabbed_object)
+{
+  if(head != NULL)
+    head->draw(context, pos, layer-2);
+  if(body != NULL)
+    body->draw(context, pos, layer-4);
+  if(arms != NULL)
+    arms->draw(context, pos, layer-1 + (grabbed_object?10:0));
+  if(feet != NULL)
+    feet->draw(context, pos, layer-3);
+}
+
+Player::Player(PlayerStatus* _player_status, const std::string& name)
+  : player_status(_player_status), grabbed_object(NULL), ghost_mode(false), climbing(0)
+{
+  this->name = name;
+  controller = main_controller;
+  smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
+  smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
+  bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
+  airarrow.reset(new Surface("images/engine/hud/airarrow.png"));
+
+  sound_manager->preload("sounds/bigjump.wav");
+  sound_manager->preload("sounds/jump.wav");
+  sound_manager->preload("sounds/hurt.wav");
+  sound_manager->preload("sounds/skid.wav");
+  sound_manager->preload("sounds/flip.wav");
+  sound_manager->preload("sounds/invincible.wav");
+  sound_manager->preload("sounds/splash.ogg");
+
+  init();
+}
+
+Player::~Player()
+{
+  if (climbing) stop_climbing(*climbing);
+  delete smalltux_gameover;
+  delete smalltux_star;
+  delete bigtux_star;
+}
+
+void
+Player::init()
+{
+  if(is_big())
+    set_size(31.8f, 62.8f);
+  else
+    set_size(31.8f, 30.8f);
+
+  dir = RIGHT;
+  old_dir = dir;
+  duck = false;
+  dead = false;
+
+  dying = false;
+  peeking = AUTO;
+  last_ground_y = 0;
+  fall_mode = ON_GROUND;
+  jumping = false;
+  can_jump = true;
+  butt_jump = false;
+  deactivated = false;
+  backflipping = false;
+  backflip_direction = 0;
+  visible = true;
+  swimming = false;
+  speedlimit = 0; //no special limit
+
+  on_ground_flag = false;
+  grabbed_object = NULL;
+
+  climbing = 0;
+
+  physic.reset();
+}
+
+void
+Player::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  Scripting::expose_object(vm, table_idx, dynamic_cast<Scripting::Player *>(this), name, false);
+}
+
+void
+Player::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+float
+Player::get_speedlimit()
+{
+  return speedlimit;
+}
+
+void
+Player::set_speedlimit(float newlimit)
+{
+  speedlimit=newlimit;
+}
+
+void
+Player::set_controller(Controller* controller)
+{
+  this->controller = controller;
+}
+
+bool
+Player::adjust_height(float new_height)
+{
+  Rect bbox2 = bbox;
+  bbox2.move(Vector(0, bbox.get_height() - new_height));
+  bbox2.set_height(new_height);
+
+  if(new_height > bbox.get_height()) {
+    Rect additional_space = bbox2;
+    additional_space.set_height(new_height - bbox.get_height());
+    if(!Sector::current()->is_free_of_statics(additional_space, this, true))
+      return false;
+  }
+
+  // adjust bbox accordingly
+  // note that we use members of moving_object for this, so we can run this during CD, too
+  set_pos(bbox2.p1);
+  set_size(bbox2.get_width(), bbox2.get_height());
+  return true;
+}
+
+void
+Player::trigger_sequence(std::string sequence_name)
+{
+  if (climbing) stop_climbing(*climbing);
+  GameSession::current()->start_sequence(sequence_name);
+}
+
+void
+Player::update(float elapsed_time)
+{
+  if( no_water ){
+    swimming = false;
+  }
+  no_water = true;
+
+  if(dying && dying_timer.check()) {
+    dead = true;
+    return;
+  }
+
+  if(!dying && !deactivated)
+    handle_input();
+
+  // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves
+  if (deactivated)
+    apply_friction();
+
+  // extend/shrink tux collision rectangle so that we fall through/walk over 1
+  // tile holes
+  if(fabsf(physic.get_velocity_x()) > MAX_WALK_XM) {
+    set_width(34);
+  } else {
+    set_width(31.8f);
+  }
+
+  // on downward slopes, adjust vertical velocity so tux walks smoothly down
+  if (on_ground()) {
+    if(floor_normal.y != 0) {
+      if ((floor_normal.x * physic.get_velocity_x()) >= 0) {
+        physic.set_velocity_y(250);
+      }
+    }
+  }
+
+  // handle backflipping
+  if (backflipping) {
+    //prevent player from changing direction when backflipping
+    dir = (backflip_direction == 1) ? LEFT : RIGHT;
+    if (backflip_timer.started()) physic.set_velocity_x(100 * backflip_direction);
+  }
+
+  // set fall mode...
+  if(on_ground()) {
+    fall_mode = ON_GROUND;
+    last_ground_y = get_pos().y;
+  } else {
+    if(get_pos().y > last_ground_y)
+      fall_mode = FALLING;
+    else if(fall_mode == ON_GROUND)
+      fall_mode = JUMPING;
+  }
+
+  // check if we landed
+  if(on_ground()) {
+    jumping = false;
+    if (backflipping && (!backflip_timer.started())) {
+      backflipping = false;
+      backflip_direction = 0;
+
+      // if controls are currently deactivated, we take care of standing up ourselves
+      if (deactivated)
+        do_standup();
+    }
+  }
+
+  // calculate movement for this frame
+  movement = physic.get_movement(elapsed_time);
+
+  if(grabbed_object != NULL && !dying) {
+    Vector pos = get_pos() +
+      Vector(dir == LEFT ? -16 : 16, get_bbox().get_height()*0.66666 - 32);
+    grabbed_object->grab(*this, pos, dir);
+  }
+
+  if(grabbed_object != NULL && dying){
+    grabbed_object->ungrab(*this, dir);
+    grabbed_object = NULL;
+  }
+
+  on_ground_flag = false;
+
+  // when invincible, spawn particles
+  if (invincible_timer.started() && !dying)
+  {
+    if (systemRandom.rand(0, 2) == 0) {
+      float px = systemRandom.randf(bbox.p1.x+0, bbox.p2.x-0);
+      float py = systemRandom.randf(bbox.p1.y+0, bbox.p2.y-0);
+      Vector ppos = Vector(px, py);
+      Vector pspeed = Vector(0, 0);
+      Vector paccel = Vector(0, 0);
+      // draw bright sparkle when there is lots of time left, dark sparkle when invincibility is about to end
+      if (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) {
+       // make every other a longer sparkle to make trail a bit fuzzy
+       if (size_t(game_time*20)%2) {
+         Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "small", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+       } else {
+         Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "medium", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+       }
+      } else {
+        Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+      }
+    }
+  }
+
+}
+
+bool
+Player::on_ground()
+{
+  return on_ground_flag;
+}
+
+bool
+Player::is_big()
+{
+  if(player_status->bonus == NO_BONUS)
+    return false;
+
+  return true;
+}
+
+void
+Player::apply_friction()
+{
+  if ((on_ground()) && (fabs(physic.get_velocity_x()) < WALK_SPEED)) {
+    physic.set_velocity_x(0);
+    physic.set_acceleration_x(0);
+  } else if(physic.get_velocity_x() < 0) {
+    physic.set_acceleration_x(WALK_ACCELERATION_X * 1.5);
+    } else if(physic.get_velocity_x() > 0) {
+    physic.set_acceleration_x(WALK_ACCELERATION_X * -1.5);
+  }
+}
+
+void
+Player::handle_horizontal_input()
+{
+  float vx = physic.get_velocity_x();
+  float vy = physic.get_velocity_y();
+  float ax = physic.get_acceleration_x();
+  float ay = physic.get_acceleration_y();
+
+  float dirsign = 0;
+  if(!duck || physic.get_velocity_y() != 0) {
+    if(controller->hold(Controller::LEFT) && !controller->hold(Controller::RIGHT)) {
+      old_dir = dir;
+      dir = LEFT;
+      dirsign = -1;
+    } else if(!controller->hold(Controller::LEFT)
+              && controller->hold(Controller::RIGHT)) {
+      old_dir = dir;
+      dir = RIGHT;
+      dirsign = 1;
+    }
+  }
+
+  // do not run if action key is pressed or we're holding something
+  // so tux can only walk while shooting
+  if ( controller->hold(Controller::ACTION) || grabbed_object ) {
+    ax = dirsign * WALK_ACCELERATION_X;
+    // limit speed
+    if(vx >= MAX_WALK_XM && dirsign > 0) {
+      vx = MAX_WALK_XM;
+      ax = 0;
+    } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
+      vx = -MAX_WALK_XM;
+      ax = 0;
+    }
+  } else {
+    if( vx * dirsign < MAX_WALK_XM ) {
+      ax = dirsign * WALK_ACCELERATION_X;
+    } else {
+      ax = dirsign * RUN_ACCELERATION_X;
+    }
+    // limit speed
+    if(vx >= MAX_RUN_XM && dirsign > 0) {
+      vx = MAX_RUN_XM;
+      ax = 0;
+    } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
+      vx = -MAX_RUN_XM;
+      ax = 0;
+    }
+  }
+
+  // we can reach WALK_SPEED without any acceleration
+  if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
+    vx = dirsign * WALK_SPEED;
+  }
+
+  //Check speedlimit.
+  if( speedlimit > 0 &&  vx * dirsign >= speedlimit ) {
+      vx = dirsign * speedlimit;
+      ax = 0;
+  }
+
+  // changing directions?
+  if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
+    // let's skid!
+    if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
+      skidding_timer.start(SKID_TIME);
+      sound_manager->play("sounds/skid.wav");
+      // dust some particles
+      Sector::current()->add_object(
+        new Particles(
+          Vector(dir == RIGHT ? get_bbox().p2.x : get_bbox().p1.x, get_bbox().p2.y),
+          dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
+          Vector(280, -260), Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f,
+          LAYER_OBJECTS+1));
+
+      ax *= 2.5;
+    } else {
+      ax *= 2;
+    }
+  }
+
+  physic.set_velocity(vx, vy);
+  physic.set_acceleration(ax, ay);
+
+  // we get slower when not pressing any keys
+  if(dirsign == 0) {
+    apply_friction();
+  }
+
+}
+
+void
+Player::do_cheer()
+{
+  do_duck();
+  do_backflip();
+  do_standup();
+}
+
+void
+Player::do_duck() {
+  if (duck)
+    return;
+  if (!is_big())
+    return;
+
+  if (physic.get_velocity_y() != 0)
+    return;
+  if (!on_ground())
+    return;
+
+  if (adjust_height(31.8f)) {
+    duck = true;
+    unduck_hurt_timer.stop();
+  } else {
+    // FIXME: what now?
+  }
+}
+
+void
+Player::do_standup() {
+  if (!duck)
+    return;
+  if (!is_big())
+    return;
+  if (backflipping)
+    return;
+
+  if (adjust_height(63.8f)) {
+    duck = false;
+    unduck_hurt_timer.stop();
+  } else {
+    // if timer is not already running, start it.
+    if (unduck_hurt_timer.get_period() == 0) {
+      unduck_hurt_timer.start(UNDUCK_HURT_TIME);
+    }
+    else if (unduck_hurt_timer.check()) {
+      kill(false);
+    }
+  }
+
+}
+
+void
+Player::do_backflip() {
+  if (!duck)
+    return;
+  if (!on_ground())
+    return;
+
+  backflip_direction = (dir == LEFT)?(+1):(-1);
+  backflipping = true;
+  do_jump(-580);
+  sound_manager->play("sounds/flip.wav");
+  backflip_timer.start(0.15f);
+}
+
+void
+Player::do_jump(float yspeed) {
+  if (!on_ground())
+    return;
+
+  physic.set_velocity_y(yspeed);
+  //bbox.move(Vector(0, -1));
+  jumping = true;
+  on_ground_flag = false;
+  can_jump = false;
+
+  // play sound
+  if (is_big()) {
+    sound_manager->play("sounds/bigjump.wav");
+  } else {
+    sound_manager->play("sounds/jump.wav");
+  }
+}
+
+void
+Player::handle_vertical_input()
+{
+  // Press jump key
+  if(controller->pressed(Controller::JUMP) && (can_jump)) {
+    if (duck) {
+      // when running, only jump a little bit; else do a backflip
+      if ((physic.get_velocity_x() != 0) || (controller->hold(Controller::LEFT)) || (controller->hold(Controller::RIGHT))) do_jump(-300); else do_backflip();
+    } else {
+      // jump a bit higher if we are running; else do a normal jump
+      if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) do_jump(-580); else do_jump(-520);
+    }
+  }
+  // Let go of jump key
+  else if(!controller->hold(Controller::JUMP)) {
+    if (!backflipping && jumping && physic.get_velocity_y() < 0) {
+      jumping = false;
+      physic.set_velocity_y(0);
+    }
+  }
+
+  /* In case the player has pressed Down while in a certain range of air,
+     enable butt jump action */
+  if (controller->hold(Controller::DOWN) && !butt_jump && !duck && is_big() && jumping) {
+    butt_jump = true;
+  }
+
+  /* When Down is not held anymore, disable butt jump */
+  if(butt_jump && !controller->hold(Controller::DOWN))
+    butt_jump = false;
+
+  // swimming
+  physic.set_acceleration_y(0);
+#ifdef SWIMMING
+  if (swimming) {
+    if (controller->hold(Controller::UP) || controller->hold(Controller::JUMP))
+      physic.set_acceleration_y(-2000);
+    physic.set_velocity_y(physic.get_velocity_y() * 0.94);
+  }
+#endif
+}
+
+void
+Player::handle_input()
+{
+  if (ghost_mode) {
+    handle_input_ghost();
+    return;
+  }
+  if (climbing) {
+    handle_input_climbing();
+    return;
+  }
+
+  /* Peeking */
+  if( controller->released( Controller::PEEK_LEFT ) ) {
+    peeking = AUTO;
+  }
+  if( controller->released( Controller::PEEK_RIGHT ) ) {
+    peeking = AUTO;
+  }
+  if( controller->released( Controller::UP ) ) {
+    peeking = AUTO;
+  }
+  if( controller->released( Controller::DOWN ) ) {
+    peeking = AUTO;
+  }
+  if( controller->pressed( Controller::PEEK_LEFT ) ) {
+    peeking = LEFT;
+  }
+  if( controller->pressed( Controller::PEEK_RIGHT ) ) {
+    peeking = RIGHT;
+  }
+  if( controller->pressed( Controller::UP ) ) {
+    peeking = UP;
+  }
+  if( controller->pressed( Controller::DOWN ) ) {
+    peeking = DOWN;
+  }
+
+  /* Handle horizontal movement: */
+  if (!backflipping) handle_horizontal_input();
+
+  /* Jump/jumping? */
+  if (on_ground() && !controller->hold(Controller::JUMP))
+    can_jump = true;
+
+  /* Handle vertical movement: */
+  handle_vertical_input();
+
+  /* Shoot! */
+  if (controller->pressed(Controller::ACTION) && (player_status->bonus == FIRE_BONUS || player_status->bonus == ICE_BONUS)) {
+    if(Sector::current()->add_bullet(
+         get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2)
+                      : Vector(32, bbox.get_height()/2)),
+         physic.get_velocity_x(), dir))
+      shooting_timer.start(SHOOTING_TIME);
+  }
+
+  /* Duck or Standup! */
+  if (controller->hold(Controller::DOWN)) {
+    do_duck();
+  } else {
+    do_standup();
+  }
+
+  /* grabbing */
+  try_grab();
+
+  if(!controller->hold(Controller::ACTION) && grabbed_object) {
+    // move the grabbed object a bit away from tux
+    Vector pos = get_pos() +
+        Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
+                bbox.get_height()*0.66666 - 32);
+    Rect dest(pos, pos + Vector(32, 32));
+    if(Sector::current()->is_free_of_movingstatics(dest)) {
+      MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
+      if(moving_object) {
+        moving_object->set_pos(pos);
+      } else {
+        log_debug << "Non MovingObject grabbed?!?" << std::endl;
+      }
+      if(controller->hold(Controller::UP)) {
+        grabbed_object->ungrab(*this, UP);
+      } else {
+        grabbed_object->ungrab(*this, dir);
+      }
+      grabbed_object = NULL;
+    }
+  }
+}
+
+void
+Player::try_grab()
+{
+  if(controller->hold(Controller::ACTION) && !grabbed_object
+      && !duck) {
+  Sector* sector = Sector::current();
+    Vector pos;
+    if(dir == LEFT) {
+      pos = Vector(bbox.get_left() - 5, bbox.get_bottom() - 16);
+    } else {
+      pos = Vector(bbox.get_right() + 5, bbox.get_bottom() - 16);
+    }
+
+    for(Sector::Portables::iterator i = sector->portables.begin();
+        i != sector->portables.end(); ++i) {
+      Portable* portable = *i;
+      if(!portable->is_portable())
+        continue;
+
+      // make sure the Portable is a MovingObject
+      MovingObject* moving_object = dynamic_cast<MovingObject*> (portable);
+      assert(moving_object);
+      if(moving_object == NULL)
+        continue;
+
+      // make sure the Portable isn't currently non-solid
+      if(moving_object->get_group() == COLGROUP_DISABLED) continue;
+
+      // check if we are within reach
+      if(moving_object->get_bbox().contains(pos)) {
+        if (climbing) stop_climbing(*climbing);
+        grabbed_object = portable;
+        grabbed_object->grab(*this, get_pos(), dir);
+        break;
+      }
+    }
+  }
+}
+
+void
+Player::handle_input_ghost()
+{
+  float vx = 0;
+  float vy = 0;
+  if (controller->hold(Controller::LEFT)) {
+    dir = LEFT;
+    vx -= MAX_RUN_XM * 2;
+  }
+  if (controller->hold(Controller::RIGHT)) {
+    dir = RIGHT;
+    vx += MAX_RUN_XM * 2;
+  }
+  if ((controller->hold(Controller::UP)) || (controller->hold(Controller::JUMP))) {
+    vy -= MAX_RUN_XM * 2;
+  }
+  if (controller->hold(Controller::DOWN)) {
+    vy += MAX_RUN_XM * 2;
+  }
+  if (controller->hold(Controller::ACTION)) {
+    set_ghost_mode(false);
+  }
+  physic.set_velocity(vx, vy);
+  physic.set_acceleration(0, 0);
+}
+
+void
+Player::add_coins(int count)
+{
+  player_status->add_coins(count);
+}
+
+int
+Player::get_coins()
+{
+  return player_status->coins;
+}
+
+bool
+Player::add_bonus(const std::string& bonustype)
+{
+  BonusType type = NO_BONUS;
+
+  if(bonustype == "grow") {
+    type = GROWUP_BONUS;
+  } else if(bonustype == "fireflower") {
+    type = FIRE_BONUS;
+  } else if(bonustype == "iceflower") {
+    type = ICE_BONUS;
+  } else if(bonustype == "none") {
+    type = NO_BONUS;
+  } else {
+    std::ostringstream msg;
+    msg << "Unknown bonus type "  << bonustype;
+    throw std::runtime_error(msg.str());
+  }
+
+  return add_bonus(type);
+}
+
+bool
+Player::add_bonus(BonusType type, bool animate)
+{
+  // always ignore NO_BONUS
+  if (type == NO_BONUS) {
+    return true;
+  }
+
+  // ignore GROWUP_BONUS if we're already big
+  if (type == GROWUP_BONUS) {
+    if (player_status->bonus == GROWUP_BONUS)
+      return true;
+    if (player_status->bonus == FIRE_BONUS)
+      return true;
+    if (player_status->bonus == ICE_BONUS)
+      return true;
+  }
+
+  return set_bonus(type, animate);
+}
+
+bool
+Player::set_bonus(BonusType type, bool animate)
+{
+  if(player_status->bonus == NO_BONUS) {
+    if (!adjust_height(62.8f)) {
+      printf("can't adjust\n");
+      return false;
+    }
+    if(animate)
+      growing_timer.start(GROWING_TIME);
+    if (climbing) stop_climbing(*climbing);
+  }
+
+  if ((type == NO_BONUS) || (type == GROWUP_BONUS)) {
+    if ((player_status->bonus == FIRE_BONUS) && (animate)) {
+      // visually lose helmet
+      Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+      Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
+      Vector paccel = Vector(0, 1000);
+      std::string action = (dir==LEFT)?"left":"right";
+      Sector::current()->add_object(new SpriteParticle("images/objects/particles/firetux-helmet.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+      if (climbing) stop_climbing(*climbing);
+    }
+    if ((player_status->bonus == ICE_BONUS) && (animate)) {
+      // visually lose cap
+      Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+      Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
+      Vector paccel = Vector(0, 1000);
+      std::string action = (dir==LEFT)?"left":"right";
+      Sector::current()->add_object(new SpriteParticle("images/objects/particles/icetux-cap.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+      if (climbing) stop_climbing(*climbing);
+    }
+    player_status->max_fire_bullets = 0;
+    player_status->max_ice_bullets = 0;
+  }
+  if (type == FIRE_BONUS) player_status->max_fire_bullets++;
+  if (type == ICE_BONUS) player_status->max_ice_bullets++;
+
+  player_status->bonus = type;
+  return true;
+}
+
+void
+Player::set_visible(bool visible)
+{
+  this->visible = visible;
+  if( visible )
+    set_group(COLGROUP_MOVING);
+  else
+    set_group(COLGROUP_DISABLED);
+}
+
+bool
+Player::get_visible()
+{
+  return visible;
+}
+
+void
+Player::kick()
+{
+  kick_timer.start(KICK_TIME);
+}
+
+void
+Player::draw(DrawingContext& context)
+{
+  if(!visible)
+    return;
+
+  // if Tux is above camera, draw little "air arrow" to show where he is x-wise
+  if (Sector::current() && Sector::current()->camera && (get_bbox().p2.y - 16 < Sector::current()->camera->get_translation().y)) {
+    float px = get_pos().x + (get_bbox().p2.x - get_bbox().p1.x - airarrow.get()->get_width()) / 2;
+    float py = Sector::current()->camera->get_translation().y;
+    py += std::min(((py - (get_bbox().p2.y + 16)) / 4), 16.0f);
+    context.draw_surface(airarrow.get(), Vector(px, py), LAYER_HUD - 1);
+  }
+
+  TuxBodyParts* tux_body;
+
+  if (player_status->bonus == GROWUP_BONUS)
+    tux_body = big_tux;
+  else if (player_status->bonus == FIRE_BONUS)
+    tux_body = fire_tux;
+  else if (player_status->bonus == ICE_BONUS)
+    tux_body = ice_tux;
+  else
+    tux_body = small_tux;
+
+  int layer = LAYER_OBJECTS + 1;
+
+  /* Set Tux sprite action */
+  if (climbing)
+    {
+    tux_body->set_action("skid-left");
+    }
+  else if (backflipping)
+    {
+    if(dir == LEFT)
+      tux_body->set_action("backflip-left");
+    else // dir == RIGHT
+      tux_body->set_action("backflip-right");
+    }
+  else if (duck && is_big())
+    {
+    if(dir == LEFT)
+      tux_body->set_action("duck-left");
+    else // dir == RIGHT
+      tux_body->set_action("duck-right");
+    }
+  else if (skidding_timer.started() && !skidding_timer.check())
+    {
+    if(dir == LEFT)
+      tux_body->set_action("skid-left");
+    else // dir == RIGHT
+      tux_body->set_action("skid-right");
+    }
+  else if (kick_timer.started() && !kick_timer.check())
+    {
+    if(dir == LEFT)
+      tux_body->set_action("kick-left");
+    else // dir == RIGHT
+      tux_body->set_action("kick-right");
+    }
+  else if (butt_jump && is_big())
+    {
+    if(dir == LEFT)
+      tux_body->set_action("buttjump-left");
+    else // dir == RIGHT
+      tux_body->set_action("buttjump-right");
+    }
+  else if (!on_ground())
+    {
+    if(dir == LEFT)
+      tux_body->set_action("jump-left");
+    else // dir == RIGHT
+      tux_body->set_action("jump-right");
+    }
+  else
+    {
+    if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
+      {
+      if(dir == LEFT)
+        tux_body->set_action("stand-left");
+      else // dir == RIGHT
+        tux_body->set_action("stand-right");
+      }
+    else // moving
+      {
+      if(dir == LEFT)
+        tux_body->set_action("walk-left");
+      else // dir == RIGHT
+        tux_body->set_action("walk-right");
+      }
+    }
+
+  if(idle_timer.check())
+    {
+    if(is_big())
+      {
+      if(dir == LEFT)
+        tux_body->head->set_action("idle-left", 1);
+      else // dir == RIGHT
+        tux_body->head->set_action("idle-right", 1);
+      }
+
+    }
+
+  // Tux is holding something
+  if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
+      (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
+    {
+    if (duck)
+      {
+      if(dir == LEFT)
+        tux_body->arms->set_action("duck+grab-left");
+      else // dir == RIGHT
+        tux_body->arms->set_action("duck+grab-right");
+      }
+    else
+      {
+      if(dir == LEFT)
+        tux_body->arms->set_action("grab-left");
+      else // dir == RIGHT
+        tux_body->arms->set_action("grab-right");
+      }
+    }
+
+  /* Draw Tux */
+  if(dying) {
+    smalltux_gameover->draw(context, get_pos(), LAYER_FLOATINGOBJECTS + 1);
+  }
+  else if ((growing_timer.get_timeleft() > 0) && (!duck)) {
+      if (dir == RIGHT) {
+        context.draw_surface(growingtux_right[int((growing_timer.get_timegone() *
+                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
+      } else {
+        context.draw_surface(growingtux_left[int((growing_timer.get_timegone() *
+                GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
+      }
+    }
+  else if (safe_timer.started() && size_t(game_time*40)%2)
+    ;  // don't draw Tux
+  else
+    tux_body->draw(context, get_pos(), layer, grabbed_object);
+
+}
+
+void
+Player::collision_tile(uint32_t tile_attributes)
+{
+  if(tile_attributes & Tile::HURTS)
+    kill(false);
+
+#ifdef SWIMMING
+  if( swimming ){
+    if( tile_attributes & Tile::WATER ){
+      no_water = false;
+    } else {
+      swimming = false;
+    }
+  } else {
+    if( tile_attributes & Tile::WATER ){
+      swimming = true;
+      no_water = false;
+      sound_manager->play( "sounds/splash.ogg" );
+    }
+  }
+#endif
+}
+
+void
+Player::collision_solid(const CollisionHit& hit)
+{
+  if(hit.bottom) {
+    if(physic.get_velocity_y() > 0)
+      physic.set_velocity_y(0);
+
+    on_ground_flag = true;
+    floor_normal = hit.slope_normal;
+  } else if(hit.top) {
+    if(physic.get_velocity_y() < 0)
+      physic.set_velocity_y(.2f);
+  }
+
+  if(hit.left || hit.right) {
+    physic.set_velocity_x(0);
+  }
+
+  // crushed?
+  if(hit.crush) {
+    if(hit.left || hit.right) {
+      kill(true);
+    } else if(hit.top || hit.bottom) {
+      kill(false);
+    }
+  }
+}
+
+HitResponse
+Player::collision(GameObject& other, const CollisionHit& hit)
+{
+  Bullet* bullet = dynamic_cast<Bullet*> (&other);
+  if(bullet) {
+    return FORCE_MOVE;
+  }
+
+  if(hit.left || hit.right) {
+    try_grab(); //grab objects right now, in update it will be too late
+  }
+#ifdef DEBUG
+  assert(dynamic_cast<MovingObject*> (&other) != NULL);
+#endif
+  MovingObject* moving_object = static_cast<MovingObject*> (&other);
+  if(moving_object->get_group() == COLGROUP_TOUCHABLE) {
+    TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
+    if(trigger) {
+      if(controller->pressed(Controller::UP))
+        trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
+    }
+
+    return FORCE_MOVE;
+  }
+
+  BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+  if(badguy != NULL) {
+    if(safe_timer.started() || invincible_timer.started())
+      return FORCE_MOVE;
+
+    return CONTINUE;
+  }
+
+  return CONTINUE;
+}
+
+void
+Player::make_invincible()
+{
+  sound_manager->play("sounds/invincible.wav");
+  invincible_timer.start(TUX_INVINCIBLE_TIME);
+  Sector::current()->play_music(HERRING_MUSIC);
+}
+
+/* Kill Player! */
+void
+Player::kill(bool completely)
+{
+  if(dying || deactivated)
+    return;
+
+  if(!completely && (safe_timer.started() || invincible_timer.started()))
+    return;
+
+  sound_manager->play("sounds/hurt.wav");
+  if (climbing) stop_climbing(*climbing);
+
+  physic.set_velocity_x(0);
+
+  if(!completely && (is_big() || growing_timer.started())) {
+    if(player_status->bonus == FIRE_BONUS
+        || player_status->bonus == ICE_BONUS) {
+      safe_timer.start(TUX_SAFE_TIME);
+      set_bonus(GROWUP_BONUS, true);
+    } else if(player_status->bonus == GROWUP_BONUS) {
+      //growing_timer.start(GROWING_TIME);
+      safe_timer.start(TUX_SAFE_TIME /* + GROWING_TIME */);
+      adjust_height(30.8f);
+      duck = false;
+      set_bonus(NO_BONUS, true);
+    } else if(player_status->bonus == NO_BONUS) {
+      growing_timer.stop();
+      safe_timer.start(TUX_SAFE_TIME);
+      adjust_height(30.8f);
+      duck = false;
+    }
+  } else {
+    if (player_status->coins >= 25 && !GameSession::current()->get_reset_point_sectorname().empty())
+    {
+      for (int i = 0; i < 5; i++)
+      {
+        // the numbers: starting x, starting y, velocity y
+        Sector::current()->add_object(new FallingCoin(get_pos() +
+              Vector(systemRandom.rand(5), systemRandom.rand(-32,18)),
+              systemRandom.rand(-100,100)));
+      }
+      player_status->coins -= std::max(player_status->coins/10, 25);
+    }
+    else
+    {
+      GameSession::current()->set_reset_point("", Vector());
+    }
+    physic.enable_gravity(true);
+    physic.set_acceleration(0, 0);
+    physic.set_velocity(0, -700);
+    set_bonus(NO_BONUS, true);
+    dying = true;
+    dying_timer.start(3.0);
+    set_group(COLGROUP_DISABLED);
+
+    DisplayEffect* effect = new DisplayEffect();
+    effect->fade_out(3.0);
+    Sector::current()->add_object(effect);
+    sound_manager->stop_music(3.0);
+  }
+}
+
+void
+Player::move(const Vector& vector)
+{
+  set_pos(vector);
+
+  // TODO: do we need the following? Seems irrelevant to moving the player
+  if(is_big())
+    set_size(31.8f, 63.8f);
+  else
+    set_size(31.8f, 31.8f);
+  duck = false;
+  last_ground_y = vector.y;
+  if (climbing) stop_climbing(*climbing);
+
+  physic.reset();
+}
+
+void
+Player::check_bounds(Camera* camera)
+{
+  /* Keep tux in bounds: */
+  if (get_pos().x < 0) {
+    // Lock Tux to the size of the level, so that he doesn't fall of
+    // on the left side
+    set_pos(Vector(0, get_pos().y));
+  }
+
+  /* fallen out of the level? */
+  if (get_pos().y > Sector::current()->get_height()) {
+    kill(true);
+    return;
+  }
+
+  // can happen if back scrolling is disabled
+  if(get_pos().x < camera->get_translation().x) {
+    set_pos(Vector(camera->get_translation().x, get_pos().y));
+  }
+  if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
+  {
+    set_pos(Vector(
+          camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
+          get_pos().y));
+  }
+}
+
+void
+Player::add_velocity(const Vector& velocity)
+{
+  physic.set_velocity(physic.get_velocity() + velocity);
+}
+
+void
+Player::add_velocity(const Vector& velocity, const Vector& end_speed)
+{
+  if (end_speed.x > 0)
+    physic.set_velocity_x(std::min(physic.get_velocity_x() + velocity.x, end_speed.x));
+  if (end_speed.x < 0)
+    physic.set_velocity_x(std::max(physic.get_velocity_x() + velocity.x, end_speed.x));
+  if (end_speed.y > 0)
+    physic.set_velocity_y(std::min(physic.get_velocity_y() + velocity.y, end_speed.y));
+  if (end_speed.y < 0)
+    physic.set_velocity_y(std::max(physic.get_velocity_y() + velocity.y, end_speed.y));
+}
+
+void
+Player::bounce(BadGuy& )
+{
+  if(controller->hold(Controller::JUMP))
+    physic.set_velocity_y(-520);
+  else
+    physic.set_velocity_y(-300);
+}
+
+//Scripting Functions Below
+
+void
+Player::deactivate()
+{
+  if (deactivated)
+    return;
+  deactivated = true;
+  physic.set_velocity_x(0);
+  physic.set_velocity_y(0);
+  physic.set_acceleration_x(0);
+  physic.set_acceleration_y(0);
+  if (climbing) stop_climbing(*climbing);
+}
+
+void
+Player::activate()
+{
+  if (!deactivated)
+    return;
+  deactivated = false;
+}
+
+void Player::walk(float speed)
+{
+  physic.set_velocity_x(speed);
+}
+
+void
+Player::set_ghost_mode(bool enable)
+{
+  if (ghost_mode == enable)
+    return;
+
+  if (climbing) stop_climbing(*climbing);
+
+  if (enable) {
+    ghost_mode = true;
+    set_group(COLGROUP_DISABLED);
+    physic.enable_gravity(false);
+    log_debug << "You feel lightheaded. Use movement controls to float around, press ACTION to scare badguys." << std::endl;
+  } else {
+    ghost_mode = false;
+    set_group(COLGROUP_MOVING);
+    physic.enable_gravity(true);
+    log_debug << "You feel solid again." << std::endl;
+  }
+}
+
+
+void 
+Player::start_climbing(Climbable& climbable)
+{
+  if (climbing == &climbable) return;
+
+  climbing = &climbable;
+  physic.enable_gravity(false);
+  physic.set_velocity(0, 0);
+  physic.set_acceleration(0, 0);
+}
+
+void 
+Player::stop_climbing(Climbable& /*climbable*/)
+{
+  if (!climbing) return;
+
+  climbing = 0;
+
+  if (grabbed_object) {    
+    grabbed_object->ungrab(*this, dir);
+    grabbed_object = NULL;
+  }
+
+  physic.enable_gravity(true);
+  physic.set_velocity(0, 0);
+  physic.set_acceleration(0, 0);
+
+  if ((controller->hold(Controller::JUMP)) || (controller->hold(Controller::UP))) {
+    on_ground_flag = true;
+    // TODO: This won't help. Why?
+    do_jump(-300);
+  }
+}
+
+void
+Player::handle_input_climbing()
+{
+  if (!climbing) {
+    log_warning << "handle_input_climbing called with climbing set to 0. Input handling skipped" << std::endl;
+    return;
+  }
+
+  float vx = 0;
+  float vy = 0;
+  if (controller->hold(Controller::LEFT)) {
+    dir = LEFT;
+    vx -= MAX_CLIMB_XM;
+  }
+  if (controller->hold(Controller::RIGHT)) {
+    dir = RIGHT;
+    vx += MAX_CLIMB_XM;
+  }
+  if (controller->hold(Controller::UP)) {
+    vy -= MAX_CLIMB_YM;
+  }
+  if (controller->hold(Controller::DOWN)) {
+    vy += MAX_CLIMB_YM;
+  }
+  if (controller->hold(Controller::JUMP)) {
+    if (can_jump) {
+      stop_climbing(*climbing);
+      return;
+    }  
+  } else {
+    can_jump = true;
+  }
+  if (controller->hold(Controller::ACTION)) {
+    stop_climbing(*climbing);
+    return;
+  }
+  physic.set_velocity(vx, vy);
+  physic.set_acceleration(0, 0);
+}
+
+
diff --git a/src/object/player.hpp b/src/object/player.hpp
new file mode 100644 (file)
index 0000000..f27decd
--- /dev/null
@@ -0,0 +1,314 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_PLAYER_H
+#define SUPERTUX_PLAYER_H
+
+#include <vector>
+#include <SDL.h>
+
+#include "timer.hpp"
+#include "direction.hpp"
+#include "video/surface.hpp"
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "physic.hpp"
+#include "control/controller.hpp"
+#include "scripting/player.hpp"
+#include "player_status.hpp"
+#include "display_effect.hpp"
+#include "script_interface.hpp"
+#include "console.hpp"
+#include "coin.hpp"
+
+class BadGuy;
+class Portable;
+class Climbable;
+
+/* Times: */
+static const float TUX_SAFE_TIME = 1.8f;
+static const float TUX_INVINCIBLE_TIME = 10.0f;
+static const float TUX_INVINCIBLE_TIME_WARNING = 2.0f;
+static const float GROWING_TIME = 0.35f;
+static const int GROWING_FRAMES = 7;
+
+class Camera;
+class PlayerStatus;
+
+extern Surface* growingtux_left[GROWING_FRAMES];
+extern Surface* growingtux_right[GROWING_FRAMES];
+
+class TuxBodyParts
+{
+public:
+  TuxBodyParts()
+    : head(0), body(0), arms(0), feet(0)
+  { }
+  ~TuxBodyParts() {
+    delete head;
+    delete body;
+    delete arms;
+    delete feet;
+  }
+
+  void set_action(std::string action, int loops = -1);
+  void one_time_animation();
+  void draw(DrawingContext& context, const Vector& pos, int layer, Portable* grabbed_object);
+
+  Sprite* head;
+  Sprite* body;
+  Sprite* arms;
+  Sprite* feet;
+};
+
+extern TuxBodyParts* small_tux;
+extern TuxBodyParts* big_tux;
+extern TuxBodyParts* fire_tux;
+extern TuxBodyParts* ice_tux;
+
+class Player : public MovingObject, public UsesPhysic, public Scripting::Player, public ScriptInterface
+{
+public:
+  enum FallMode { ON_GROUND, JUMPING, TRAMPOLINE_JUMP, FALLING };
+
+  Controller* controller;
+  PlayerStatus* player_status;
+  bool duck;
+  bool dead;
+  //Tux can only go this fast. If set to 0 no special limit is used, only the default limits.
+  void set_speedlimit(float newlimit);
+  float get_speedlimit();
+
+private:
+  bool dying;
+  bool backflipping;
+  int  backflip_direction;
+  Direction peeking;
+  bool swimming;
+  float speedlimit;
+
+public:
+  Direction dir;
+  Direction old_dir;
+
+  float last_ground_y;
+  FallMode fall_mode;
+
+  bool on_ground_flag;
+  bool jumping;
+  bool can_jump;
+  bool butt_jump;
+
+  Timer invincible_timer;
+  Timer skidding_timer;
+  Timer safe_timer;
+  Timer kick_timer;
+  Timer shooting_timer;   // used to show the arm when Tux is shooting
+  Timer dying_timer;
+  Timer growing_timer;
+  Timer idle_timer;
+  Timer backflip_timer;
+
+public:
+  Player(PlayerStatus* player_status, const std::string& name);
+  virtual ~Player();
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  void set_controller(Controller* controller);
+  Controller* get_controller()
+  {
+    return controller;
+  }
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+  virtual void collision_solid(const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+  virtual void collision_tile(uint32_t tile_attributes);
+
+  void make_invincible();
+  bool is_invincible() const
+  {
+    return invincible_timer.started();
+  }
+  bool is_dying() const
+  {
+    return dying;
+  }
+  Direction peeking_direction() const
+  {
+    return peeking;
+  }
+
+  void kill(bool completely);
+  void check_bounds(Camera* camera);
+  void move(const Vector& vector);
+
+  virtual bool add_bonus(const std::string& bonus);
+  virtual void add_coins(int count);
+  virtual int get_coins();
+
+  /**
+   * picks up a bonus, taking care not to pick up lesser bonus items than we already have
+   *
+   * @returns true if the bonus has been set (or was already good enough)
+   *          false if the bonus could not be set (for example no space for big tux)
+   */
+  bool add_bonus(BonusType type, bool animate = false);
+  /**
+   * like add_bonus, but can also downgrade the bonus items carried
+   */
+  bool set_bonus(BonusType type, bool animate = false);
+
+  PlayerStatus* get_status()
+  {
+    return player_status;
+  }
+  // set kick animation
+  void kick();
+
+  /**
+   * play cheer animation.
+   * This might need some space and behave in an unpredictable way. Best to use this at level end.
+   */
+  void do_cheer();
+
+  /**
+   * duck down if possible.
+   * this won't last long as long as input is enabled.
+   */
+  void do_duck();
+
+  /**
+   * stand back up if possible.
+   */
+  void do_standup();
+
+  /**
+   * do a backflip if possible.
+   */
+  void do_backflip();
+
+  /**
+   * jump in the air if possible
+   * sensible values for yspeed are negative - unless we want to jump into the ground of course
+   */
+  void do_jump(float yspeed);
+
+  /**
+   * Adds velocity to the player (be carefull when using this)
+   */
+  void add_velocity(const Vector& velocity);
+
+  /**
+   * Adds velocity to the player until given end speed is reached
+   */
+  void add_velocity(const Vector& velocity, const Vector& end_speed);
+
+  void bounce(BadGuy& badguy);
+
+  bool is_dead() const
+  { return dead; }
+  bool is_big();
+
+  void set_visible(bool visible);
+  bool get_visible();
+
+  bool on_ground();
+
+  Portable* get_grabbed_object() const
+  {
+      return grabbed_object;
+  }
+
+  /**
+   * Switches ghost mode on/off.
+   * Lets Tux float around and through solid objects.
+   */
+  void set_ghost_mode(bool enable);
+
+  /**
+   * Returns whether ghost mode is currently enabled
+   */
+  bool get_ghost_mode() { return ghost_mode; }
+
+  /**
+   * Changes height of bounding box.
+   * Returns true if successful, false otherwise
+   */
+  bool adjust_height(float new_height);
+
+  /**
+   * Orders the current GameSession to start a sequence
+   */
+  void trigger_sequence(std::string sequence_name);
+  
+  /**
+   * Requests that the player start climbing the given Climbable
+   */
+  void start_climbing(Climbable& climbable);
+
+  /**
+   * Requests that the player stop climbing the given Climbable
+   */
+  void stop_climbing(Climbable& climbable);
+
+private:
+  void handle_input();
+  void handle_input_ghost(); /**< input handling while in ghost mode */
+  void handle_input_climbing(); /**< input handling while climbing */
+  bool deactivated;
+
+  void init();
+
+  void handle_horizontal_input();
+  void handle_vertical_input();
+
+  void activate();
+  void deactivate();
+  void walk(float speed);
+
+  /**
+   * slows Tux down a little, based on where he's standing
+   */
+  void apply_friction();
+
+  bool visible;
+
+  Portable* grabbed_object;
+
+  Sprite* smalltux_gameover;
+  Sprite* smalltux_star;
+  Sprite* bigtux_star;
+
+  std::auto_ptr<Surface> airarrow; /**< arrow indicating Tux' position when he's above the camera */
+
+  Vector floor_normal;
+  void try_grab();
+
+  bool ghost_mode; /**< indicates if Tux should float around and through solid objects */
+
+  Timer unduck_hurt_timer; /**< if Tux wants to stand up again after ducking and cannot, this timer is started */
+
+  Climbable* climbing; /**< Climbable object we are currently climbing, null if none */
+};
+
+#endif /*SUPERTUX_PLAYER_H*/
diff --git a/src/object/pneumatic_platform.cpp b/src/object/pneumatic_platform.cpp
new file mode 100644 (file)
index 0000000..79d3198
--- /dev/null
@@ -0,0 +1,117 @@
+//  $Id$
+//
+//  SuperTux - PneumaticPlatform
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "pneumatic_platform.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "object/portable.hpp"
+
+PneumaticPlatform::PneumaticPlatform(const lisp::Lisp& reader)
+       : MovingSprite(reader, LAYER_OBJECTS, COLGROUP_STATIC), 
+       master(0), slave(0), start_y(0), offset_y(0), speed_y(0)
+{
+  start_y = get_pos().y;
+}
+
+PneumaticPlatform::PneumaticPlatform(PneumaticPlatform* master)
+       : MovingSprite(*master), 
+       master(master), slave(this), start_y(master->start_y), offset_y(-master->offset_y), speed_y(0)
+{
+  set_pos(get_pos() + Vector(master->get_bbox().get_width(), 0));
+  master->master = master;
+  master->slave = this;
+}
+
+PneumaticPlatform::~PneumaticPlatform() {
+  if ((this == master) && (master)) {
+    slave->master = 0;
+    slave->slave = 0;
+  }
+  if ((master) && (this == slave)) {
+    master->master = 0;
+    master->slave = 0;
+  }
+  master = 0;
+  slave = 0;
+}
+
+HitResponse
+PneumaticPlatform::collision(GameObject& other, const CollisionHit& )
+{
+
+  // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this:
+  MovingObject* mo = dynamic_cast<MovingObject*>(&other);
+  if (!mo) return FORCE_MOVE;
+  if ((mo->get_bbox().p2.y) > (get_bbox().p1.y + 2)) return FORCE_MOVE;
+
+  Player* pl = dynamic_cast<Player*>(mo);
+  if (pl) {
+    if (pl->is_big()) contacts.insert(0);
+    Portable* po = pl->get_grabbed_object();
+    MovingObject* pomo = dynamic_cast<MovingObject*>(po);
+    if (pomo) contacts.insert(pomo);
+  }
+
+  contacts.insert(&other);
+  return FORCE_MOVE;
+}
+
+void
+PneumaticPlatform::update(float elapsed_time)
+{
+  if (!slave) {
+    Sector::current()->add_object(new PneumaticPlatform(this));
+    return;
+  }
+  if (!master) {
+    return;
+  }
+  if (this == slave) {
+    offset_y = -master->offset_y;
+    movement = Vector(0, (start_y + offset_y) - get_pos().y);
+  }
+  if (this == master) {
+    int contact_diff = contacts.size() - slave->contacts.size();
+    contacts.clear();
+    slave->contacts.clear();
+
+    speed_y += ((float)contact_diff * elapsed_time) * 128.0f;
+    speed_y -= (offset_y * elapsed_time * 0.5f);
+    speed_y *= 1 - elapsed_time;
+    offset_y += speed_y * elapsed_time;
+    if (offset_y < -256) { offset_y = -256; speed_y = 0; } 
+    if (offset_y > 256) { offset_y = 256; speed_y = -0; } 
+    movement = Vector(0, (start_y + offset_y) - get_pos().y);
+  }
+}
+
+IMPLEMENT_FACTORY(PneumaticPlatform, "pneumatic-platform");
+
diff --git a/src/object/pneumatic_platform.hpp b/src/object/pneumatic_platform.hpp
new file mode 100644 (file)
index 0000000..07936fb
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux - PneumaticPlatform
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PNEUMATIC_PLATFORM_H__
+#define __PNEUMATIC_PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include <set>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+
+/**
+ * Used to construct a pair of pneumatic platforms: If one is pushed down, the other one rises
+ */
+class PneumaticPlatform : public MovingSprite
+{
+public:
+  PneumaticPlatform(const lisp::Lisp& reader);
+  PneumaticPlatform(PneumaticPlatform* master);
+  virtual ~PneumaticPlatform();
+
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+  virtual void update(float elapsed_time);
+
+protected:
+  PneumaticPlatform* master; /**< pointer to PneumaticPlatform that does movement calculation */
+  PneumaticPlatform* slave; /**< pointer to PneumaticPlatform that reacts to master platform's movement calculation */
+  float start_y; /**< vertical start position */
+  float offset_y; /**< vertical offset from the start position in px */
+  float speed_y; /**< vertical speed */
+  std::set<GameObject*> contacts; /**< objects that are currently pushing on the platform */
+
+};
+
+#endif
diff --git a/src/object/portable.hpp b/src/object/portable.hpp
new file mode 100644 (file)
index 0000000..721879d
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PORTABLE_H__
+#define __PORTABLE_H__
+
+#include "moving_object.hpp"
+#include "direction.hpp"
+#include "refcounter.hpp"
+
+/**
+ * An object that inherits from this object is considered "portable" and can
+ * be carried around by the player.
+ * The object has to additionally set the PORTABLE flag (this allows to
+ * make the object only temporarily portable by resetting the flag)
+ */
+class Portable
+{
+public:
+  virtual ~Portable()
+  { }
+
+  /**
+   * called each frame when the object has been grabbed.
+   */
+  virtual void grab(MovingObject& object, const Vector& pos, Direction dir) = 0;
+
+  virtual void ungrab(MovingObject& , Direction )
+  {}
+
+  virtual bool is_portable() const
+  {
+    return true;
+  }
+};
+
+#endif
diff --git a/src/object/powerup.cpp b/src/object/powerup.cpp
new file mode 100644 (file)
index 0000000..09a7470
--- /dev/null
@@ -0,0 +1,95 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include <math.h>
+#include <stdexcept>
+#include "powerup.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+
+PowerUp::PowerUp(const lisp::Lisp& lisp)
+  : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING)
+{
+  lisp.get("script", script);
+  no_physics = false;
+  lisp.get("disable-physics", no_physics);
+  physic.enable_gravity(true);
+  sound_manager->preload("sounds/grow.wav");
+  sound_manager->preload("sounds/fire-flower.wav");
+}
+
+void
+PowerUp::collision_solid(const CollisionHit& hit)
+{
+  if(hit.bottom) {
+    physic.set_velocity_y(0);
+  }
+  if(hit.right || hit.left) {
+    physic.set_velocity_x(-physic.get_velocity_x());
+  }
+}
+
+HitResponse
+PowerUp::collision(GameObject& other, const CollisionHit&)
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if(player == 0)
+    return FORCE_MOVE;
+
+  if (script != "") {
+    std::istringstream stream(script);
+    Sector::current()->run_script(stream, "powerup-script");
+    remove_me();
+    return ABORT_MOVE;
+  }
+
+  // some defaults if no script has been set
+  if (sprite_name == "images/powerups/egg/egg.sprite") {
+    if(!player->add_bonus(GROWUP_BONUS, true))
+      return FORCE_MOVE;
+    sound_manager->play("sounds/grow.wav");
+  } else if (sprite_name == "images/powerups/fireflower/fireflower.sprite") {
+    if(!player->add_bonus(FIRE_BONUS, true))
+      return FORCE_MOVE;
+    sound_manager->play("sounds/fire-flower.wav");
+  } else if (sprite_name == "images/powerups/star/star.sprite") {
+    player->make_invincible();
+  } else if (sprite_name == "images/powerups/1up/1up.sprite") {
+    player->get_status()->add_coins(100);
+  }
+
+  remove_me();
+  return ABORT_MOVE;
+}
+
+void
+PowerUp::update(float elapsed_time)
+{
+  if (!no_physics)
+    movement = physic.get_movement(elapsed_time);
+}
+
+IMPLEMENT_FACTORY(PowerUp, "powerup");
diff --git a/src/object/powerup.hpp b/src/object/powerup.hpp
new file mode 100644 (file)
index 0000000..4779db1
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __POWERUP_H__
+#define __POWERUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "collision_hit.hpp"
+#include "physic.hpp"
+
+class PowerUp : public MovingSprite, private UsesPhysic
+{
+public:
+  PowerUp(const lisp::Lisp& lisp);
+
+  virtual void update(float elapsed_time);
+  virtual void collision_solid(const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+  std::string script;
+  bool no_physics;
+};
+
+#endif
diff --git a/src/object/pulsing_light.cpp b/src/object/pulsing_light.cpp
new file mode 100644 (file)
index 0000000..c5d5872
--- /dev/null
@@ -0,0 +1,58 @@
+//  $Id$
+//
+//  SuperTux - Pulsing Light
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "pulsing_light.hpp"
+#include "video/color.hpp"
+#include <math.h>
+#include "random_generator.hpp"
+
+PulsingLight::PulsingLight(const Vector& center, float cycle_len, float min_alpha, float max_alpha, const Color& color)
+       : Light(center, color), min_alpha(min_alpha), max_alpha(max_alpha), cycle_len(cycle_len), t(0)
+{
+  assert(cycle_len > 0);
+
+  // start with random phase offset
+  t = systemRandom.randf(0.0, cycle_len);
+}
+
+PulsingLight::~PulsingLight()
+{
+}
+
+void
+PulsingLight::update(float elapsed_time)
+{
+  Light::update(elapsed_time);
+
+  t += elapsed_time;
+  if (t > cycle_len) t -= cycle_len;
+}
+
+void
+PulsingLight::draw(DrawingContext& context)
+{
+  Color old_color = color;
+
+  color.alpha *= min_alpha + ((max_alpha - min_alpha) * cos(2 * M_PI * t / cycle_len));
+  Light::draw(context);
+
+  color = old_color;
+}
diff --git a/src/object/pulsing_light.hpp b/src/object/pulsing_light.hpp
new file mode 100644 (file)
index 0000000..f395968
--- /dev/null
@@ -0,0 +1,51 @@
+//  $Id$
+//
+//  SuperTux - Pulsing Light
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PULSING_LIGHT_HPP__
+#define __PULSING_LIGHT_HPP__
+
+#include "light.hpp"
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+/**
+ * Light source that changes alpha value to give the impression of a pulsating light
+ */
+class PulsingLight : public Light
+{
+public:
+  PulsingLight(const Vector& center, float cycle_len = 5.0, float min_alpha = 0.0, float max_alpha = 1.0, const Color& color = Color(1.0, 1.0, 1.0, 1.0));
+  virtual ~PulsingLight();
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+protected:
+  float min_alpha; /**< minimum alpha */
+  float max_alpha; /**< maximum alpha */
+  float cycle_len; /**< length in seconds of one cycle */
+
+  float t; /**< local time in seconds */
+};
+
+#endif
diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp
new file mode 100644 (file)
index 0000000..30e364c
--- /dev/null
@@ -0,0 +1,82 @@
+//  $Id$
+//
+//  SuperTux - PushButton running a script
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <stdexcept>
+
+#include "pushbutton.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+
+namespace {
+  const std::string BUTTON_SOUND = "sounds/switch.ogg";
+ //14 -> 8
+}
+
+PushButton::PushButton(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/pushbutton/pushbutton.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_MOVING), state(OFF)
+{
+  sound_manager->preload(BUTTON_SOUND);
+  set_action("off", -1);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+  if (!lisp.get("script", script)) throw std::runtime_error("no script set");
+}
+
+void
+PushButton::update(float /*elapsed_time*/)
+{
+}
+
+HitResponse
+PushButton::collision(GameObject& other, const CollisionHit& hit)
+{
+  Player* player = dynamic_cast<Player*>(&other);
+  if (!player) return FORCE_MOVE;
+  float vy = player->physic.get_velocity_y();
+
+  //player->add_velocity(Vector(0, -150));
+  player->physic.set_velocity_y(-150);
+
+  if (state != OFF) return FORCE_MOVE;
+  if (!hit.top) return FORCE_MOVE;
+  if (vy <= 0) return FORCE_MOVE;
+
+  // change appearance
+  state = ON;
+  float old_bbox_height = bbox.get_height();
+  set_action("on", -1);
+  float new_bbox_height = bbox.get_height();
+  set_pos(get_pos() + Vector(0, old_bbox_height - new_bbox_height));
+
+  // play sound
+  sound_manager->play(BUTTON_SOUND);
+
+  // run script
+  std::istringstream stream(script);
+  Sector::current()->run_script(stream, "PushButton");
+
+  return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(PushButton, "pushbutton");
diff --git a/src/object/pushbutton.hpp b/src/object/pushbutton.hpp
new file mode 100644 (file)
index 0000000..55f3fc7
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux - PushButton running a script
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_BUTTON_H
+#define SUPERTUX_BUTTON_H
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+/**
+ * PushButton - jump on it to run a script
+ */
+class PushButton : public MovingSprite
+{
+public:
+  PushButton(const lisp::Lisp& reader);
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+
+private:
+  enum PushButtonState {
+    OFF,
+    ON
+  };
+
+  std::string script;
+  PushButtonState state;
+};
+
+#endif
diff --git a/src/object/rainsplash.cpp b/src/object/rainsplash.cpp
new file mode 100644 (file)
index 0000000..c69f817
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "rainsplash.hpp"
+#include "sector.hpp"
+
+RainSplash::RainSplash(Vector pos, bool vertical)
+{
+  frame = 0;
+  position = pos;
+  if (vertical) sprite = sprite_manager->create("images/objects/particles/rainsplash-vertical.sprite");
+  else sprite = sprite_manager->create("images/objects/particles/rainsplash.sprite");
+}
+
+RainSplash::~RainSplash() {
+  remove_me();
+}
+
+void
+RainSplash::hit(Player& )
+{
+}
+
+void
+RainSplash::update(float time)
+{
+  time = 0;//just so i don't get an "unused variable" error - don't know how to circumvent this
+  frame++;
+  if (frame >= 10) remove_me();
+}
+
+void
+RainSplash::draw(DrawingContext& context)
+{
+   sprite->draw(context, position, LAYER_OBJECTS);
+}
diff --git a/src/object/rainsplash.hpp b/src/object/rainsplash.hpp
new file mode 100644 (file)
index 0000000..61e438f
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __RAINSPLASH_H__
+#define __RAINSPLASH_H__
+
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+class RainSplash : public GameObject
+{
+public:
+  RainSplash(Vector pos, bool vertical);
+  ~RainSplash();
+protected:
+  virtual void hit(Player& );
+  virtual void update(float time);
+  virtual void draw(DrawingContext& context);
+private:
+  Sprite* sprite;
+  Vector position;
+  int frame;
+};
+
+#endif
diff --git a/src/object/rock.cpp b/src/object/rock.cpp
new file mode 100644 (file)
index 0000000..7c56e1a
--- /dev/null
@@ -0,0 +1,145 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "rock.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "audio/sound_manager.hpp"
+#include "tile.hpp"
+
+namespace {
+  const std::string ROCK_SOUND = "sounds/brick.wav"; //TODO use own sound.
+}
+
+Rock::Rock(const Vector& pos, std::string spritename)
+  : MovingSprite(pos, spritename)
+{
+  sound_manager->preload(ROCK_SOUND);
+  on_ground = false;
+  grabbed = false;
+  set_group(COLGROUP_MOVING_STATIC);
+}
+
+Rock::Rock(const lisp::Lisp& reader)
+  : MovingSprite(reader, "images/objects/rock/rock.sprite")
+{
+  sound_manager->preload(ROCK_SOUND);
+  on_ground = false;
+  grabbed = false;
+  set_group(COLGROUP_MOVING_STATIC);
+}
+
+Rock::Rock(const lisp::Lisp& reader, std::string spritename)
+  : MovingSprite(reader, spritename)
+{
+  sound_manager->preload(ROCK_SOUND);
+  on_ground = false;
+  grabbed = false;
+  set_group(COLGROUP_MOVING_STATIC);
+}
+
+void
+Rock::write(lisp::Writer& writer)
+{
+  writer.start_list("rock");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+
+  writer.end_list("rock");
+}
+
+void
+Rock::update(float elapsed_time)
+{
+  if( grabbed )
+    return;
+
+  if (on_ground) physic.set_velocity_x(0);
+
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+Rock::collision_solid(const CollisionHit& hit)
+{
+  if(grabbed) {
+    return;
+  }
+  if(hit.top || hit.bottom)
+    physic.set_velocity_y(0);
+  if(hit.left || hit.right)
+    physic.set_velocity_x(0);
+  if(hit.crush)
+    physic.set_velocity(0, 0);
+
+  if(hit.bottom  && !on_ground && !grabbed) {
+    sound_manager->play(ROCK_SOUND, get_pos());
+    on_ground = true;
+  }
+}
+
+HitResponse
+Rock::collision(GameObject& other, const CollisionHit& hit)
+{
+  if(grabbed) {
+    return PASSTHROUGH;
+  }
+  if(!on_ground) {
+    if(hit.bottom && physic.get_velocity_y() > 200) {
+      MovingObject* moving_object = dynamic_cast<MovingObject*> (&other);
+      if(moving_object) {
+        //Getting a rock on the head hurts. A lot.
+        moving_object->collision_tile(Tile::HURTS);
+      }
+    }
+    return FORCE_MOVE;
+  }
+
+  return FORCE_MOVE;
+}
+
+void
+Rock::grab(MovingObject& , const Vector& pos, Direction)
+{
+  movement = pos - get_pos();
+  last_movement = movement;
+  set_group(COLGROUP_TOUCHABLE);
+  on_ground = false;
+  grabbed = true;
+}
+
+void
+Rock::ungrab(MovingObject& , Direction dir)
+{
+  set_group(COLGROUP_MOVING_STATIC);
+  on_ground = false;
+  if(dir == UP) {
+    physic.set_velocity(0, -500);
+  } else if (last_movement.norm() > 1) {
+    physic.set_velocity((dir == RIGHT) ? 200 : -200, -200);
+  } else {
+    physic.set_velocity(0, 0);
+  }
+  grabbed = false;
+}
+
+IMPLEMENT_FACTORY(Rock, "rock");
diff --git a/src/object/rock.hpp b/src/object/rock.hpp
new file mode 100644 (file)
index 0000000..86a8e0e
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ROCK_H__
+#define __ROCK_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "lisp/lisp.hpp"
+#include "portable.hpp"
+#include "serializable.hpp"
+
+class Sprite;
+
+class Rock : public MovingSprite, public Portable, protected UsesPhysic, public Serializable
+{
+public:
+  Rock(const Vector& pos, std::string spritename);
+  Rock(const lisp::Lisp& reader);
+  Rock(const lisp::Lisp& reader, std::string spritename);
+  virtual Rock* clone() const { return new Rock(*this); }
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+  void write(lisp::Writer& writer);
+
+  void grab(MovingObject& object, const Vector& pos, Direction dir);
+  void ungrab(MovingObject& object, Direction dir);
+
+protected:
+  bool on_ground;
+  bool grabbed;
+  Vector last_movement;
+};
+
+#endif
diff --git a/src/object/scripted_object.cpp b/src/object/scripted_object.cpp
new file mode 100644 (file)
index 0000000..eadcc43
--- /dev/null
@@ -0,0 +1,210 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stdexcept>
+#include <math.h>
+
+#include "scripted_object.hpp"
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "resources.hpp"
+#include "object_factory.hpp"
+#include "math/vector.hpp"
+
+ScriptedObject::ScriptedObject(const lisp::Lisp& lisp)
+  : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING_STATIC),
+    solid(true), physic_enabled(true), visible(true), new_vel_set(false)
+{
+  lisp.get("name", name);
+  if(name == "")
+    throw std::runtime_error("Scripted object must have a name specified");
+
+  // FIXME: do we need this? bbox is already set via .sprite file
+  float width = sprite->get_width();
+  float height = sprite->get_height();
+  lisp.get("width", width);
+  lisp.get("height", height);
+  bbox.set_size(width, height);
+
+  lisp.get("solid", solid);
+  lisp.get("physic-enabled", physic_enabled);
+  lisp.get("visible", visible);
+  lisp.get("z-pos", layer);
+  if( solid ){
+    set_group( COLGROUP_MOVING_STATIC );
+  } else {
+    set_group( COLGROUP_DISABLED );
+  }
+}
+
+void
+ScriptedObject::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  expose_object(vm, table_idx, dynamic_cast<Scripting::ScriptedObject *>(this), name, false);
+}
+
+void
+ScriptedObject::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+ScriptedObject::move(float x, float y)
+{
+  bbox.move(Vector(x, y));
+}
+
+void
+ScriptedObject::set_pos(float x, float y)
+{
+  printf("SetPos: %f %f\n", x, y);
+  bbox.set_pos(Vector(x, y));
+  physic.reset();
+}
+
+float
+ScriptedObject::get_pos_x()
+{
+  return get_pos().x;
+}
+
+float
+ScriptedObject::get_pos_y()
+{
+  return get_pos().y;
+}
+
+void
+ScriptedObject::set_velocity(float x, float y)
+{
+  new_vel = Vector(x, y);
+  new_vel_set = true;
+}
+
+float
+ScriptedObject::get_velocity_x()
+{
+  return physic.get_velocity_x();
+}
+
+float
+ScriptedObject::get_velocity_y()
+{
+  return physic.get_velocity_y();
+}
+
+void
+ScriptedObject::set_visible(bool visible)
+{
+  this->visible = visible;
+}
+
+bool
+ScriptedObject::is_visible()
+{
+  return visible;
+}
+
+void
+ScriptedObject::set_solid(bool solid)
+{
+  this->solid = solid;
+  if( solid ){
+    set_group( COLGROUP_MOVING_STATIC );
+  } else {
+    set_group( COLGROUP_DISABLED );
+  }
+}
+
+bool
+ScriptedObject::is_solid()
+{
+  return solid;
+}
+
+
+void
+ScriptedObject::set_action(const std::string& animation)
+{
+  sprite->set_action(animation);
+}
+
+std::string
+ScriptedObject::get_action()
+{
+  return sprite->get_action();
+}
+
+std::string
+ScriptedObject::get_name()
+{
+  return name;
+}
+
+void
+ScriptedObject::update(float elapsed_time)
+{
+  if(!physic_enabled)
+    return;
+
+  if(new_vel_set) {
+    physic.set_velocity(new_vel.x, new_vel.y);
+    new_vel_set = false;
+  }
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+ScriptedObject::draw(DrawingContext& context)
+{
+  if(!visible)
+    return;
+
+  sprite->draw(context, get_pos(), layer);
+}
+
+void
+ScriptedObject::collision_solid(const CollisionHit& hit)
+{
+  if(!physic_enabled)
+    return;
+
+  if(hit.bottom) {
+    if(physic.get_velocity_y() > 0)
+      physic.set_velocity_y(0);
+  } else if(hit.top) {
+    physic.set_velocity_y(.1f);
+  }
+
+  if(hit.left || hit.right) {
+    physic.set_velocity_x(0);
+  }
+}
+
+HitResponse
+ScriptedObject::collision(GameObject& , const CollisionHit& )
+{
+  return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(ScriptedObject, "scriptedobject");
diff --git a/src/object/scripted_object.hpp b/src/object/scripted_object.hpp
new file mode 100644 (file)
index 0000000..e7d6f9d
--- /dev/null
@@ -0,0 +1,74 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTED_OBJECT_H__
+#define __SCRIPTED_OBJECT_H__
+
+#include <string>
+#include "physic.hpp"
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "script_interface.hpp"
+#include "scripting/scripted_object.hpp"
+
+class ScriptedObject : public MovingSprite, public UsesPhysic,
+                       public Scripting::ScriptedObject, public ScriptInterface
+{
+public:
+  ScriptedObject(const lisp::Lisp& lisp);
+  virtual ScriptedObject* clone() const { return new ScriptedObject(*this); }
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+  void collision_solid(const CollisionHit& hit);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  // --- Scripting Interface stuff ---
+
+  void set_action(const std::string& animation);
+  std::string get_action();
+
+  void move(float x, float y);
+  void set_pos(float x, float y);
+  float get_pos_x();
+  float get_pos_y();
+  void set_velocity(float x, float y);
+  float get_velocity_x();
+  float get_velocity_y();
+  void set_visible(bool visible);
+  bool is_visible();
+  void set_solid(bool solid);
+  bool is_solid();
+
+  std::string get_name();
+
+private:
+  std::string name;
+  bool solid;
+  bool physic_enabled;
+  bool visible;
+  bool new_vel_set;
+  Vector new_vel;
+};
+
+#endif
diff --git a/src/object/skull_tile.cpp b/src/object/skull_tile.cpp
new file mode 100644 (file)
index 0000000..025b5e8
--- /dev/null
@@ -0,0 +1,84 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "skull_tile.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+
+static const float CRACKTIME = 0.3f;
+static const float FALLTIME = 0.8f;
+
+SkullTile::SkullTile(const lisp::Lisp& lisp)
+       : MovingSprite(lisp, "images/objects/skull_tile/skull_tile.sprite", LAYER_TILES, COLGROUP_STATIC), hit(false), falling(false)
+{
+}
+
+HitResponse
+SkullTile::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player)
+    hit = true;
+
+  return FORCE_MOVE;
+}
+
+void
+SkullTile::draw(DrawingContext& context)
+{
+  Vector pos = get_pos();
+  // shacking
+  if(timer.get_timegone() > CRACKTIME) {
+    pos.x += systemRandom.rand(-3, 3);
+  }
+
+  sprite->draw(context, pos, layer);
+}
+
+void
+SkullTile::update(float elapsed_time)
+{
+  if(falling) {
+    movement = physic.get_movement(elapsed_time);
+    if(!Sector::current()->inside(bbox)) {
+      remove_me();
+      return;
+    }
+  } else if(hit) {
+    if(timer.check()) {
+      falling = true;
+      physic.enable_gravity(true);
+      timer.stop();
+    } else if(!timer.started()) {
+      timer.start(FALLTIME);
+    }
+  } else {
+    timer.stop();
+  }
+  hit = false;
+}
+
+IMPLEMENT_FACTORY(SkullTile, "skull_tile");
diff --git a/src/object/skull_tile.hpp b/src/object/skull_tile.hpp
new file mode 100644 (file)
index 0000000..5a65798
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SKULL_TILE_H__
+#define __SKULL_TILE_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+class Player;
+
+/** A tile that starts falling down if tux stands to long on it */
+class SkullTile : public MovingSprite, private UsesPhysic
+{
+public:
+  SkullTile(const lisp::Lisp& lisp);
+  virtual SkullTile* clone() const { return new SkullTile(*this); }
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+private:
+  Timer timer;
+  bool hit;
+  bool falling;
+};
+
+#endif
diff --git a/src/object/specialriser.cpp b/src/object/specialriser.cpp
new file mode 100644 (file)
index 0000000..60c7861
--- /dev/null
@@ -0,0 +1,58 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <math.h>
+#include "specialriser.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "sprite/sprite_manager.hpp"
+
+SpecialRiser::SpecialRiser(Vector pos, MovingObject* _child)
+  : child(_child)
+{
+  _child->set_pos(pos - Vector(0, 32));
+  offset = 0;
+}
+
+SpecialRiser::~SpecialRiser()
+{
+}
+
+void
+SpecialRiser::update(float elapsed_time)
+{
+  offset += 50 * elapsed_time;
+  if(offset > 32) {
+    Sector::current()->add_object(child);
+    remove_me();
+  }
+}
+
+void
+SpecialRiser::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(
+      context.get_translation() + Vector(0, -32 + offset));
+  child->draw(context);
+  context.pop_transform();
+}
diff --git a/src/object/specialriser.hpp b/src/object/specialriser.hpp
new file mode 100644 (file)
index 0000000..03f9347
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SPECIALRISE_H__
+#define __SPECIALRISE_H__
+
+#include "moving_object.hpp"
+
+/**
+ * special object that contains another object and slowly rises it out of a
+ * bonus block.
+ */
+class SpecialRiser : public GameObject
+{
+public:
+  SpecialRiser(Vector pos, MovingObject* child);
+  ~SpecialRiser();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+private:
+  float offset;
+  MovingObject* child;
+};
+
+#endif
diff --git a/src/object/spotlight.cpp b/src/object/spotlight.cpp
new file mode 100644 (file)
index 0000000..d86848c
--- /dev/null
@@ -0,0 +1,98 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "spotlight.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+
+Spotlight::Spotlight(const lisp::Lisp& lisp)
+  : angle(0.0f),
+    color(1.0f, 1.0f, 1.0f)
+{
+  lisp.get("x", position.x);
+  lisp.get("y", position.y);
+
+  lisp.get("angle", angle);
+
+  std::vector<float> vColor;
+  if( lisp.get_vector( "color", vColor ) ){
+    color = Color( vColor );
+  }
+
+  center    = sprite_manager->create("images/objects/spotlight/spotlight_center.sprite");
+  base      = sprite_manager->create("images/objects/spotlight/spotlight_base.sprite");
+  lights    = sprite_manager->create("images/objects/spotlight/spotlight_lights.sprite");
+  lightcone = sprite_manager->create("images/objects/spotlight/lightcone.sprite");
+  light     = sprite_manager->create("images/objects/spotlight/light.sprite");
+
+
+}
+
+Spotlight::~Spotlight()
+{
+  delete center;
+  delete base;
+  delete lights;
+  delete lightcone;
+  delete light;
+}
+
+void
+Spotlight::update(float delta)
+{
+  angle += delta * 50.0f;
+}
+
+void
+Spotlight::draw(DrawingContext& context)
+{
+  context.push_target();
+  context.set_target(DrawingContext::LIGHTMAP);
+
+  light->set_color(color);
+  light->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+  light->set_angle(angle);
+  light->draw(context, position, 0);
+
+  //lightcone->set_angle(angle);
+  //lightcone->draw(context, position, 0);
+
+  context.set_target(DrawingContext::NORMAL);
+
+  lights->set_angle(angle);
+  lights->draw(context, position, 0);
+
+  base->set_angle(angle);
+  base->draw(context, position, 0);
+
+  center->draw(context, position, 0);
+
+  lightcone->set_angle(angle);
+  lightcone->draw(context, position, LAYER_FOREGROUND1 + 10);
+
+  context.pop_target();
+}
+
+IMPLEMENT_FACTORY(Spotlight, "spotlight");
diff --git a/src/object/spotlight.hpp b/src/object/spotlight.hpp
new file mode 100644 (file)
index 0000000..b48ddfd
--- /dev/null
@@ -0,0 +1,51 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SPOTLIGHT_HPP__
+#define __SPOTLIGHT_HPP__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+class Spotlight : public GameObject
+{
+public:
+  Spotlight(const lisp::Lisp& reader);
+  virtual ~Spotlight();
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+private:
+  Vector  position;
+  float   angle;
+  Sprite* center;
+  Sprite* base;
+  Sprite* lights;
+  Sprite* light;
+  Sprite* lightcone;
+
+  Color   color;
+};
+
+#endif
diff --git a/src/object/sprite_particle.cpp b/src/object/sprite_particle.cpp
new file mode 100644 (file)
index 0000000..e914c0c
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stdexcept>
+#include "sprite_particle.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+SpriteParticle::SpriteParticle(std::string sprite_name, std::string action, Vector position, AnchorPoint anchor, Vector velocity, Vector acceleration, int drawing_layer)
+       : position(position), velocity(velocity), acceleration(acceleration), drawing_layer(drawing_layer)
+{
+  sprite = sprite_manager->create(sprite_name);
+  if (!sprite) throw std::runtime_error("Could not load sprite "+sprite_name);
+  sprite->set_action(action, 1);
+  sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action
+
+  this->position -= get_anchor_pos(sprite->get_current_hitbox(), anchor);
+}
+
+SpriteParticle::~SpriteParticle()
+{
+  remove_me();
+}
+
+void
+SpriteParticle::hit(Player& )
+{
+}
+
+void
+SpriteParticle::update(float elapsed_time)
+{
+  // die when animation is complete
+  if (sprite->animation_done()) {
+    remove_me();
+    return;
+  }
+
+  // calculate new position and velocity
+  position.x += velocity.x * elapsed_time;
+  position.y += velocity.y * elapsed_time;
+  velocity.x += acceleration.x * elapsed_time;
+  velocity.y += acceleration.y * elapsed_time;
+
+  // die when too far offscreen
+  Vector camera = Sector::current()->camera->get_translation();
+  if ((position.x < camera.x - 128) || (position.x > SCREEN_WIDTH + camera.x + 128) ||
+      (position.y < camera.y - 128) || (position.y > SCREEN_HEIGHT + camera.y + 128)) {
+    remove_me();
+    return;
+  }
+}
+
+void
+SpriteParticle::draw(DrawingContext& context)
+{
+   sprite->draw(context, position, drawing_layer);
+}
diff --git a/src/object/sprite_particle.hpp b/src/object/sprite_particle.hpp
new file mode 100644 (file)
index 0000000..dba9caa
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SPRITE_PARTICLE_H__
+#define __SPRITE_PARTICLE_H__
+
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "object/anchor_point.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+class SpriteParticle : public GameObject
+{
+public:
+  SpriteParticle(std::string sprite_name, std::string action, Vector position, AnchorPoint anchor, Vector velocity, Vector acceleration, int drawing_layer = LAYER_OBJECTS-1);
+  ~SpriteParticle();
+protected:
+  virtual void hit(Player& player);
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+private:
+  Sprite* sprite;
+  Vector position;
+  Vector velocity;
+  Vector acceleration;
+  int drawing_layer;
+};
+
+#endif
diff --git a/src/object/star.cpp b/src/object/star.cpp
new file mode 100644 (file)
index 0000000..316f572
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "star.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "player_status.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+static const float INITIALJUMP = -400;
+static const float SPEED = 150;
+static const float JUMPSPEED = -300;
+
+Star::Star(const Vector& pos, Direction direction)
+       : MovingSprite(pos, "images/powerups/star/star.sprite", LAYER_OBJECTS, COLGROUP_MOVING)
+{
+  physic.set_velocity((direction == LEFT) ? -SPEED : SPEED, INITIALJUMP);
+}
+
+void
+Star::update(float elapsed_time)
+{
+  movement = physic.get_movement(elapsed_time);
+}
+
+void
+Star::collision_solid(const CollisionHit& hit)
+{
+  if(hit.bottom) {
+    physic.set_velocity_y(JUMPSPEED);
+  } else if(hit.top) {
+    physic.set_velocity_y(0);
+  } else if(hit.left || hit.right) {
+    physic.set_velocity_x(-physic.get_velocity_x());
+  }
+}
+
+HitResponse
+Star::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player) {
+    player->make_invincible();
+    remove_me();
+    return ABORT_MOVE;
+  }
+
+  return FORCE_MOVE;
+}
diff --git a/src/object/star.hpp b/src/object/star.hpp
new file mode 100644 (file)
index 0000000..e3f4f04
--- /dev/null
@@ -0,0 +1,38 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __STAR_H__
+#define __STAR_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class Star : public MovingSprite, private UsesPhysic
+{
+public:
+  Star(const Vector& pos, Direction direction = RIGHT);
+  virtual Star* clone() const { return new Star(*this); }
+
+  virtual void update(float elapsed_time);
+  virtual void collision_solid(const CollisionHit& hit);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
diff --git a/src/object/text_object.cpp b/src/object/text_object.cpp
new file mode 100644 (file)
index 0000000..4271628
--- /dev/null
@@ -0,0 +1,161 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "text_object.hpp"
+
+#include <iostream>
+#include "resources.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "log.hpp"
+
+TextObject::TextObject(std::string name)
+  : fading(0), fadetime(0), visible(false), anchor(ANCHOR_MIDDLE),
+    pos(0, 0)
+{
+  this->name = name;
+  font = blue_text;
+  centered = false;
+}
+
+TextObject::~TextObject()
+{
+}
+
+void
+TextObject::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  Scripting::expose_object(vm, table_idx, dynamic_cast<Scripting::Text *>(this), name, false);
+}
+
+void
+TextObject::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty())
+    return;
+
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+TextObject::set_font(const std::string& name)
+{
+  if(name == "gold") {
+    font = gold_text;
+  } else if(name == "white") {
+    font = white_text;
+  } else if(name == "blue") {
+    font = blue_text;
+  } else if(name == "gray") {
+    font = gray_text;
+  } else if(name == "big") {
+    font = white_big_text;
+  } else if(name == "small") {
+    font = white_small_text;
+  } else {
+    log_warning << "Unknown font '" << name << "'." << std::endl;
+  }
+}
+
+void
+TextObject::set_text(const std::string& text)
+{
+  this->text = text;
+}
+
+void
+TextObject::fade_in(float fadetime)
+{
+  this->fadetime = fadetime;
+  fading = fadetime;
+}
+
+void
+TextObject::fade_out(float fadetime)
+{
+  this->fadetime = fadetime;
+  fading = -fadetime;
+}
+
+void
+TextObject::set_visible(bool visible)
+{
+  this->visible = visible;
+  fading = 0;
+}
+
+void
+TextObject::set_centered(bool centered)
+{
+  this->centered = centered;
+}
+
+void
+TextObject::draw(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+  if(fading > 0) {
+    context.set_alpha((fadetime-fading) / fadetime);
+  } else if(fading < 0) {
+    context.set_alpha(-fading / fadetime);
+  } else if(!visible) {
+    context.pop_transform();
+    return;
+  }
+
+  float width  = 500;
+  float height = 70;
+  Vector spos = pos + get_anchor_pos(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+      width, height, anchor);
+
+  context.draw_filled_rect(spos, Vector(width, height),
+      Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-50);
+  if (centered) {
+    context.draw_center_text(font, text, spos, LAYER_GUI-40);
+  } else {
+    context.draw_text(font, text, spos + Vector(10, 10), ALIGN_LEFT, LAYER_GUI-40);
+  }
+
+  context.pop_transform();
+}
+
+void
+TextObject::update(float elapsed_time)
+{
+  if(fading > 0) {
+    fading -= elapsed_time;
+    if(fading <= 0) {
+      fading = 0;
+      visible = true;
+    }
+  } else if(fading < 0) {
+    fading += elapsed_time;
+    if(fading >= 0) {
+      fading = 0;
+      visible = false;
+    }
+  }
+}
diff --git a/src/object/text_object.hpp b/src/object/text_object.hpp
new file mode 100644 (file)
index 0000000..b5e0862
--- /dev/null
@@ -0,0 +1,93 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __TEXTOBJECT_H__
+#define __TEXTOBJECT_H__
+
+#include "game_object.hpp"
+#include "scripting/text.hpp"
+#include "script_interface.hpp"
+#include "anchor_point.hpp"
+
+class Font;
+
+/** A text object intended for scripts that want to tell a story */
+class TextObject : public GameObject, public Scripting::Text,
+                   public ScriptInterface
+{
+public:
+  TextObject(std::string name = "");
+  virtual ~TextObject();
+
+  void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  void set_text(const std::string& text);
+  void set_font(const std::string& name);
+  void fade_in(float fadetime);
+  void fade_out(float fadetime);
+  void set_visible(bool visible);
+  void set_centered(bool centered);
+  bool is_visible();
+
+  void set_anchor_point(AnchorPoint anchor) {
+    this->anchor = anchor;
+  }
+  AnchorPoint get_anchor_point() const {
+    return anchor;
+  }
+
+  void set_pos(const Vector& pos) {
+    this->pos = pos; 
+  } 
+  void set_pos(float x, float y) {
+    set_pos(Vector(x, y));
+  }
+  const Vector& get_pos() const {
+    return pos; 
+  }
+  float get_pos_x() {
+    return pos.x;
+  }
+  float get_pos_y() {
+    return pos.y;
+  }
+
+  void set_anchor_point(int anchor) {
+    set_anchor_point((AnchorPoint) anchor);
+  }
+  int get_anchor_point() {
+    return (int) get_anchor_point();
+  }
+
+  void draw(DrawingContext& context);
+  void update(float elapsed_time);
+
+private:
+  Font* font;
+  std::string text;
+  float fading;
+  float fadetime;
+  bool visible;
+  bool centered;
+  AnchorPoint anchor;
+  Vector pos;
+};
+
+#endif
diff --git a/src/object/thunderstorm.cpp b/src/object/thunderstorm.cpp
new file mode 100644 (file)
index 0000000..656bd0a
--- /dev/null
@@ -0,0 +1,151 @@
+//  $Id$
+//
+//  SuperTux - Thunderstorm Game Object
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "thunderstorm.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "audio/sound_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "object/electrifier.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object/player.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+namespace {
+       const float LIGHTNING_DELAY = 2.0f;
+       const float FLASH_DISPLAY_TIME = 0.1f;
+}
+
+Thunderstorm::Thunderstorm(const lisp::Lisp& reader)
+  : running(true), interval(10.0f)
+{
+  reader.get("name", name);
+  reader.get("running", running);
+  reader.get("interval", interval);
+  if(interval <= 0) {
+    log_warning << "Running a thunderstorm with non-positive time interval is a bad idea" << std::endl;
+  }
+
+  sound_manager->preload("sounds/explosion.wav");
+  sound_manager->preload("sounds/upgrade.wav");
+
+  if (running) {
+    running = false; // else start() is ignored
+    start();
+  }
+}
+
+void
+Thunderstorm::update(float )
+{
+  if (!running) return;
+  if (time_to_thunder.check()) {
+    thunder();
+    time_to_lightning.start(LIGHTNING_DELAY);
+  }
+  if (time_to_lightning.check()) {
+    lightning();
+    time_to_thunder.start(interval);
+  }
+}
+
+void
+Thunderstorm::draw(DrawingContext& context)
+{
+  if (!flash_display_timer.started()) return;
+
+  float alpha = 0.33f;
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+  context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT), Color(1, 1, 1, alpha), LAYER_BACKGROUNDTILES-1);
+  context.pop_transform();
+
+}
+
+void
+Thunderstorm::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name == "") return;
+  Scripting::Thunderstorm* interface = new Scripting::Thunderstorm(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Thunderstorm::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name == "") return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Thunderstorm::start()
+{
+  if (running) return;
+  running = true;
+  time_to_thunder.start(interval);
+  time_to_lightning.stop();
+}
+
+void
+Thunderstorm::stop()
+{
+  if (!running) return;
+  running = false;
+  time_to_thunder.stop();
+  time_to_lightning.stop();
+}
+
+void
+Thunderstorm::thunder()
+{
+  sound_manager->play("sounds/explosion.wav");
+}
+
+void
+Thunderstorm::lightning()
+{
+  flash();
+  electrify();
+}
+
+void
+Thunderstorm::flash()
+{
+  sound_manager->play("sounds/upgrade.wav");
+  sound_manager->play("sounds/explosion.wav");
+  flash_display_timer.start(FLASH_DISPLAY_TIME);
+}
+
+void
+Thunderstorm::electrify()
+{
+  Sector::current()->add_object(new Electrifier(75, 1421, 0.5));
+  Sector::current()->add_object(new Electrifier(76, 1422, 0.5));
+}
+
+IMPLEMENT_FACTORY(Thunderstorm, "thunderstorm");
diff --git a/src/object/thunderstorm.hpp b/src/object/thunderstorm.hpp
new file mode 100644 (file)
index 0000000..5a7fbe8
--- /dev/null
@@ -0,0 +1,91 @@
+//  $Id$
+//
+//  SuperTux - Thunderstorm Game Object
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __THUNDERSTORM_H__
+#define __THUNDERSTORM_H__
+
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "scripting/thunderstorm.hpp"
+#include "script_interface.hpp"
+
+/**
+ * Thunderstorm scriptable GameObject; plays thunder, lightning and electrifies water at regular interval
+ */
+class Thunderstorm : public GameObject, public ScriptInterface
+{
+public:
+    Thunderstorm(const lisp::Lisp& reader);
+
+    void update(float elapsed_time);
+    void draw(DrawingContext& context);
+
+    virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+    virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+    /**
+     * @name Scriptable Methods
+     * @{
+     */
+
+    /**
+     * Start playing thunder and lightning at configured interval
+     */
+    void start();
+
+    /**
+     * Stop playing thunder and lightning at configured interval
+     */
+    void stop();
+
+    /**
+     * Play thunder
+     */
+    void thunder();
+
+    /**
+     * Play lightning, i.e. call flash() and electrify()
+     */
+    void lightning();
+
+    /**
+     * Display a nice flash
+     */
+    void flash();
+
+    /**
+     * Electrify water throughout the whole sector for a short time
+     */
+    void electrify();
+
+    /**
+     * @}
+     */
+
+private:
+    bool running; /**< whether we currently automatically trigger lightnings */
+    float interval; /**< time between two lightnings */
+
+    Timer time_to_thunder; /**< counts down until next thunder */
+    Timer time_to_lightning; /**< counts down until next lightning */
+    Timer flash_display_timer; /**< counts down while flash is displayed */
+};
+
+#endif
diff --git a/src/object/tilemap.cpp b/src/object/tilemap.cpp
new file mode 100644 (file)
index 0000000..576cf48
--- /dev/null
@@ -0,0 +1,366 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <cassert>
+#include <algorithm>
+#include <iostream>
+#include <stdexcept>
+#include <math.h>
+
+#include "tilemap.hpp"
+#include "video/drawing_context.hpp"
+#include "level.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "tile_manager.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "scripting/tilemap.hpp"
+#include "scripting/squirrel_util.hpp"
+
+TileMap::TileMap()
+  : solid(false), speed_x(1), speed_y(1), width(0), height(0), z_pos(0), x_offset(0), y_offset(0),
+    drawing_effect(NO_EFFECT), alpha(1.0), current_alpha(1.0), remaining_fade_time(0),
+    draw_target(DrawingContext::NORMAL)
+{
+  tilemanager = tile_manager;
+}
+
+TileMap::TileMap(const lisp::Lisp& reader, TileManager* new_tile_manager)
+  : solid(false), speed_x(1), speed_y(1), width(-1), height(-1), z_pos(0),
+    x_offset(0), y_offset(0),
+    drawing_effect(NO_EFFECT), alpha(1.0), current_alpha(1.0),
+    remaining_fade_time(0),
+    draw_target(DrawingContext::NORMAL)
+{
+  tilemanager = new_tile_manager;
+  if(tilemanager == 0)
+    tilemanager = tile_manager;
+
+  reader.get("name", name);
+  reader.get("z-pos", z_pos);
+  reader.get("solid", solid);
+  reader.get("speed", speed_x);
+  reader.get("speed-y", speed_y);
+
+  if(solid && ((speed_x != 1) || (speed_y != 1))) {
+    log_warning << "Speed of solid tilemap is not 1. fixing" << std::endl;
+    speed_x = 1;
+    speed_y = 1;
+  }
+
+  const lisp::Lisp* pathLisp = reader.get_lisp("path");
+  if (pathLisp) {
+    path.reset(new Path());
+    path->read(*pathLisp);
+    walker.reset(new PathWalker(path.get(), /*running*/false));
+    Vector v = path->get_base();
+    set_x_offset(v.x);
+    set_y_offset(v.y);
+  }
+
+  std::string draw_target_s = "normal";
+  reader.get("draw-target", draw_target_s);
+  if (draw_target_s == "normal") draw_target = DrawingContext::NORMAL;
+  if (draw_target_s == "lightmap") draw_target = DrawingContext::LIGHTMAP;
+
+  if (reader.get("alpha", alpha)) {
+    current_alpha = alpha;
+  }
+
+  reader.get("width", width);
+  reader.get("height", height);
+  if(width < 0 || height < 0)
+    throw std::runtime_error("Invalid/No width/height specified in tilemap.");
+
+  if(!reader.get_vector("tiles", tiles))
+    throw std::runtime_error("No tiles in tilemap.");
+
+  if(int(tiles.size()) != width*height) {
+    throw std::runtime_error("wrong number of tiles in tilemap.");
+  }
+
+  // make sure all tiles are loaded
+  for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+    tilemanager->get(*i);
+}
+
+TileMap::TileMap(std::string name, int z_pos, bool solid, size_t width, size_t height)
+  : solid(solid), speed_x(1), speed_y(1), width(0), height(0), z_pos(z_pos),
+    x_offset(0), y_offset(0), drawing_effect(NO_EFFECT), alpha(1.0),
+    current_alpha(1.0), remaining_fade_time(0),
+    draw_target(DrawingContext::NORMAL)
+{
+  this->name = name;
+  tilemanager = tile_manager;
+
+  resize(width, height);
+}
+
+TileMap::~TileMap()
+{
+}
+
+void
+TileMap::write(lisp::Writer& writer)
+{
+  writer.start_list("tilemap");
+
+  writer.write_int("z-pos", z_pos);
+
+  writer.write_bool("solid", solid);
+  writer.write_float("speed", speed_x);
+  writer.write_float("speed-y", speed_y);
+  writer.write_int("width", width);
+  writer.write_int("height", height);
+  writer.write_int_vector("tiles", tiles);
+
+  writer.end_list("tilemap");
+}
+
+void
+TileMap::update(float elapsed_time)
+{
+  // handle tilemap fading
+  if (current_alpha != alpha) {
+    remaining_fade_time = std::max(0.0f, remaining_fade_time - elapsed_time);
+    if (remaining_fade_time == 0.0f) {
+      current_alpha = alpha;
+    } else {
+      float amt = (alpha - current_alpha) / (remaining_fade_time / elapsed_time);
+      if (amt > 0) current_alpha = std::min(current_alpha + amt, alpha);
+      if (amt < 0) current_alpha = std::max(current_alpha + amt, alpha);
+    }
+    if ((alpha < 0.25) && (current_alpha < 0.25)) set_solid(false);
+    if ((alpha > 0.75) && (current_alpha > 0.75)) set_solid(true);
+  }
+
+  // if we have a path to follow, follow it
+  if (walker.get()) {
+    Vector v = walker->advance(elapsed_time);
+    set_x_offset(v.x);
+    set_y_offset(v.y);
+  }
+}
+
+void
+TileMap::draw(DrawingContext& context)
+{
+  // skip draw if current opacity is set to 0.0
+  if (current_alpha == 0.0) return;
+
+  context.push_transform();
+  context.push_target();
+  context.set_target(draw_target);
+
+  if(drawing_effect != 0) context.set_drawing_effect(drawing_effect);
+  if(current_alpha != 1.0) context.set_alpha(current_alpha);
+
+  float trans_x = roundf(context.get_translation().x);
+  float trans_y = roundf(context.get_translation().y);
+  context.set_translation(Vector(trans_x * speed_x, trans_y * speed_y));
+
+  /** if we don't round here, we'll have a 1 pixel gap on screen sometimes.
+   * I have no idea why */
+  float start_x = int((roundf(context.get_translation().x) - roundf(x_offset)) / 32) * 32 + roundf(x_offset);
+  float start_y = int((roundf(context.get_translation().y) - roundf(y_offset)) / 32) * 32 + roundf(y_offset);
+  float end_x = std::min(start_x + SCREEN_WIDTH + 32, float(width * 32 + roundf(x_offset)));
+  float end_y = std::min(start_y + SCREEN_HEIGHT + 32, float(height * 32 + roundf(y_offset)));
+  int tsx = int((start_x - roundf(x_offset)) / 32); // tilestartindex x
+  int tsy = int((start_y - roundf(y_offset)) / 32); // tilestartindex y
+
+  Vector pos;
+  int tx, ty;
+  for(pos.x = start_x, tx = tsx; pos.x < end_x; pos.x += 32, ++tx) {
+    for(pos.y = start_y, ty = tsy; pos.y < end_y; pos.y += 32, ++ty) {
+      if ((tx < 0) || (ty < 0)) continue;
+      const Tile* tile = tilemanager->get(tiles[ty*width + tx]);
+      assert(tile != 0);
+      tile->draw(context, pos, z_pos);
+    }
+  }
+
+  context.pop_target();
+  context.pop_transform();
+}
+
+void
+TileMap::goto_node(int node_no)
+{
+  if (!walker.get()) return;
+  walker->goto_node(node_no);
+}
+
+void
+TileMap::start_moving()
+{
+  if (!walker.get()) return;
+  walker->start_moving();
+}
+
+void
+TileMap::stop_moving()
+{
+  if (!walker.get()) return;
+  walker->stop_moving();
+}
+
+void
+TileMap::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::TileMap* interface = new Scripting::TileMap(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+TileMap::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name.empty()) return;
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+TileMap::set(int newwidth, int newheight, const std::vector<unsigned int>&newt,
+    int new_z_pos, bool newsolid)
+{
+  if(int(newt.size()) != newwidth * newheight)
+    throw std::runtime_error("Wrong tilecount count.");
+
+  width  = newwidth;
+  height = newheight;
+
+  tiles.resize(newt.size());
+  tiles = newt;
+
+  z_pos  = new_z_pos;
+  solid  = newsolid;
+
+  // make sure all tiles are loaded
+  for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+    tilemanager->get(*i);
+}
+
+void
+TileMap::resize(int new_width, int new_height, int fill_id)
+{
+  if(new_width < width) {
+    // remap tiles for new width
+    for(int y = 0; y < height && y < new_height; ++y) {
+      for(int x = 0; x < new_width; ++x) {
+        tiles[y * new_width + x] = tiles[y * width + x];
+      }
+    }
+  }
+
+  tiles.resize(new_width * new_height, fill_id);
+
+  if(new_width > width) {
+    // remap tiles
+    for(int y = std::min(height, new_height)-1; y >= 0; --y) {
+      for(int x = new_width-1; x >= 0; --x) {
+        if(x >= width) {
+          tiles[y * new_width + x] = fill_id;
+          continue;
+        }
+
+        tiles[y * new_width + x] = tiles[y * width + x];
+      }
+    }
+  }
+
+  height = new_height;
+  width = new_width;
+}
+
+void
+TileMap::set_solid(bool solid)
+{
+  this->solid = solid;
+}
+
+const Tile*
+TileMap::get_tile(int x, int y) const
+{
+  if(x < 0 || x >= width || y < 0 || y >= height) {
+    //log_warning << "tile outside tilemap requested" << std::endl;
+    return tilemanager->get(0);
+  }
+
+  return tilemanager->get(tiles[y*width + x]);
+}
+
+const Tile*
+TileMap::get_tile_at(const Vector& pos) const
+{
+  return get_tile(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32);
+}
+
+void
+TileMap::change(int x, int y, uint32_t newtile)
+{
+  assert(x >= 0 && x < width && y >= 0 && y < height);
+  tiles[y*width + x] = newtile;
+}
+
+void
+TileMap::change_at(const Vector& pos, uint32_t newtile)
+{
+  change(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32, newtile);
+}
+
+void
+TileMap::change_all(uint32_t oldtile, uint32_t newtile)
+{
+  for (size_t x = 0; x < get_width(); x++)
+    for (size_t y = 0; y < get_height(); y++) {
+      if (get_tile(x,y)->getID() == oldtile) change(x,y,newtile);
+    }
+}
+
+void
+TileMap::fade(float alpha, float seconds)
+{
+  this->alpha = alpha;
+  this->remaining_fade_time = seconds;
+}
+
+
+void 
+TileMap::set_alpha(float alpha)
+{
+  this->alpha = alpha;
+  this->current_alpha = alpha;
+  this->remaining_fade_time = 0;
+  if (current_alpha < 0.25) set_solid(false);
+  if (current_alpha > 0.75) set_solid(true);
+}
+
+float 
+TileMap::get_alpha()
+{
+  return this->current_alpha;
+}
+
+IMPLEMENT_FACTORY(TileMap, "tilemap");
diff --git a/src/object/tilemap.hpp b/src/object/tilemap.hpp
new file mode 100644 (file)
index 0000000..e80fb73
--- /dev/null
@@ -0,0 +1,176 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_TILEMAP_H
+#define SUPERTUX_TILEMAP_H
+
+#include <vector>
+#include <stdint.h>
+#include <string>
+
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Level;
+class TileManager;
+class Tile;
+
+/**
+ * This class is reponsible for drawing the level tiles
+ */
+class TileMap : public GameObject, public Serializable, public ScriptInterface
+{
+public:
+  TileMap();
+  TileMap(const lisp::Lisp& reader, TileManager* tile_manager = 0);
+  TileMap(std::string name, int z_pos, bool solid_, size_t width_, size_t height_);
+  virtual ~TileMap();
+
+  virtual void write(lisp::Writer& writer);
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+  /** Move tilemap until at given node, then stop */
+  void goto_node(int node_no);
+
+  /** Start moving tilemap */
+  void start_moving();
+
+  /** Stop tilemap at next node */
+  void stop_moving();
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+  void set(int width, int height, const std::vector<unsigned int>& vec,
+      int z_pos, bool solid);
+
+  /** resizes the tilemap to a new width and height (tries to not destroy the
+   * existing map)
+   */
+  void resize(int newwidth, int newheight, int fill_id = 0);
+
+  size_t get_width() const
+  { return width; }
+
+  size_t get_height() const
+  { return height; }
+
+  float get_x_offset() const
+  { return x_offset; }
+
+  float get_y_offset() const
+  { return y_offset; }
+
+  void set_x_offset(float x_offset)
+  { this->x_offset = x_offset; }
+
+  void set_y_offset(float y_offset)
+  { this->y_offset = y_offset; }
+
+  int get_layer() const
+  { return z_pos; }
+
+  bool is_solid() const
+  { return solid; }
+
+  /**
+   * Changes Tilemap's solidity, i.e. whether to consider it when doing collision detection.
+   */
+  void set_solid(bool solid = true);
+
+  /// returns tile in row y and column y (of the tilemap)
+  const Tile* get_tile(int x, int y) const;
+  /// returns tile at position pos (in world coordinates)
+  const Tile* get_tile_at(const Vector& pos) const;
+
+  void change(int x, int y, uint32_t newtile);
+
+  void change_at(const Vector& pos, uint32_t newtile);
+
+  /// changes all tiles with the given ID
+  void change_all(uint32_t oldtile, uint32_t newtile);
+
+  TileManager* get_tilemanager() const
+  {
+    return tilemanager;
+  }
+
+  void set_drawing_effect(DrawingEffect effect)
+  {
+    drawing_effect = effect;
+  }
+
+  DrawingEffect get_drawing_effect()
+  {
+    return drawing_effect;
+  }
+
+  /**
+   * Start fading the tilemap to opacity given by @c alpha.
+   * Destination opacity will be reached after @c seconds seconds. Also influences solidity.
+   */
+  void fade(float alpha, float seconds = 0);
+
+  /**
+   * Instantly switch tilemap's opacity to @c alpha. Also influences solidity.
+   */
+  void set_alpha(float alpha);
+
+  /**
+   * Return tilemap's opacity. Note that while the tilemap is fading in or out, this will return the current alpha value, not the target alpha.
+   */
+  float get_alpha();
+
+private:
+  typedef std::vector<uint32_t> Tiles;
+  Tiles tiles;
+
+private:
+  TileManager* tilemanager;
+  bool solid;
+  float speed_x;
+  float speed_y;
+  int width, height;
+  int z_pos;
+  float x_offset;
+  float y_offset;
+
+  DrawingEffect drawing_effect;
+  float alpha; /**< requested tilemap opacity */
+  float current_alpha; /**< current tilemap opacity */
+  float remaining_fade_time; /**< seconds until requested tilemap opacity is reached */
+
+  std::auto_ptr<Path> path;
+  std::auto_ptr<PathWalker> walker;
+
+  DrawingContext::Target draw_target; /**< set to LIGHTMAP to draw to lightmap */
+};
+
+#endif /*SUPERTUX_TILEMAP_H*/
diff --git a/src/object/trampoline.cpp b/src/object/trampoline.cpp
new file mode 100644 (file)
index 0000000..4871cf6
--- /dev/null
@@ -0,0 +1,127 @@
+//  $Id$
+//
+//  SuperTux - Trampoline
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "trampoline.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "badguy/walking_badguy.hpp"
+
+/* Trampoline will accelerate player to to VY_INITIAL, if
+ * he jumps on it to VY_MIN. */
+namespace {
+  const std::string TRAMPOLINE_SOUND = "sounds/trampoline.wav";
+  const float VY_MIN = -900; //negative, upwards
+  const float VY_INITIAL = -500;
+}
+
+Trampoline::Trampoline(const lisp::Lisp& lisp)
+       : Rock(lisp, "images/objects/trampoline/trampoline.sprite")
+{
+  sound_manager->preload(TRAMPOLINE_SOUND);
+
+  portable = true;
+  //Check if this trampoline is not portable
+  if(lisp.get("portable", portable)) {
+    if(!portable) {
+        //we need another sprite
+        sprite_name = "images/objects/trampoline/trampoline_fix.sprite";
+        sprite = sprite_manager->create(sprite_name);
+        sprite->set_action("normal");
+    }
+  }
+}
+
+void
+Trampoline::update(float elapsed_time)
+{
+  if(sprite->animation_done()) {
+    sprite->set_action("normal");
+  }
+
+  Rock::update(elapsed_time);
+}
+
+HitResponse
+Trampoline::collision(GameObject& other, const CollisionHit& hit)
+{
+
+  //Tramponine has to be on ground to work.
+  if(on_ground) {
+    Player* player = dynamic_cast<Player*> (&other);
+    //Trampoline works for player
+    if(player) {
+      float vy = player->physic.get_velocity_y();
+      //player is falling down on trampoline
+      if(hit.top && vy >= 0) {
+       if(player->get_controller()->hold(Controller::JUMP)) {
+         vy = VY_MIN;
+       } else {
+         vy = VY_INITIAL;
+       }
+       player->physic.set_velocity_y(vy);
+       sound_manager->play(TRAMPOLINE_SOUND);
+       sprite->set_action("swinging", 1);
+       return FORCE_MOVE;
+      }
+    }
+    WalkingBadguy* walking_badguy = dynamic_cast<WalkingBadguy*> (&other);
+    //Trampoline also works for WalkingBadguy
+    if(walking_badguy) {
+      float vy = walking_badguy->get_velocity_y();
+      //walking_badguy is falling down on trampoline
+      if(hit.top && vy >= 0) {
+       vy = VY_INITIAL;
+       walking_badguy->set_velocity_y(vy);
+       sound_manager->play(TRAMPOLINE_SOUND);
+       sprite->set_action("swinging", 1);
+       return FORCE_MOVE;
+      }
+    }
+  }
+
+  return Rock::collision(other, hit);
+}
+
+void
+Trampoline::collision_solid(const CollisionHit& hit) {
+  Rock::collision_solid(hit);
+}
+
+void
+Trampoline::grab(MovingObject& object, const Vector& pos, Direction dir) {
+  sprite->set_animation_loops(0);
+  Rock::grab(object, pos, dir);
+}
+
+void
+Trampoline::ungrab(MovingObject& object, Direction dir) {
+  Rock::ungrab(object, dir);
+}
+
+bool
+Trampoline::is_portable() const
+{
+  return Rock::is_portable() && portable;
+}
+
+IMPLEMENT_FACTORY(Trampoline, "trampoline");
diff --git a/src/object/trampoline.hpp b/src/object/trampoline.hpp
new file mode 100644 (file)
index 0000000..c7653c0
--- /dev/null
@@ -0,0 +1,48 @@
+//  $Id$
+//
+//  SuperTux - Trampolin
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_TRAMPOLINE_H
+#define SUPERTUX_TRAMPOLINE_H
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object/rock.hpp"
+
+/**
+ * Jumping on a trampolin makes tux jump higher.
+ */
+class Trampoline : public Rock
+{
+public:
+  Trampoline(const lisp::Lisp& reader);
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void collision_solid(const CollisionHit& hit);
+  void update(float elapsed_time);
+
+  void grab(MovingObject&, const Vector& pos, Direction);
+  void ungrab(MovingObject&, Direction);
+  bool is_portable() const;
+
+private:
+  bool portable;
+
+};
+
+#endif
diff --git a/src/object/unstable_tile.cpp b/src/object/unstable_tile.cpp
new file mode 100644 (file)
index 0000000..089a18d
--- /dev/null
@@ -0,0 +1,80 @@
+//  $Id$
+//
+//  SuperTux - Unstable Tile
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "unstable_tile.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+#include "object/bullet.hpp"
+
+UnstableTile::UnstableTile(const lisp::Lisp& lisp)
+  : MovingSprite(lisp, LAYER_TILES, COLGROUP_STATIC), state(STATE_NORMAL)
+{
+  sprite->set_action("normal");
+}
+
+HitResponse
+UnstableTile::collision(GameObject& other, const CollisionHit& )
+{
+  if(state == STATE_NORMAL) {
+    Player* player = dynamic_cast<Player*> (&other);
+    if(player != NULL &&
+        player->get_bbox().get_bottom() < get_bbox().get_top() + 7.0) {
+      state = STATE_CRUMBLING;
+      sprite->set_action("crumbling", 1);
+    }
+  }
+  return SOLID;
+}
+
+void
+UnstableTile::update(float elapsed_time)
+{
+  switch (state) {
+
+    case STATE_NORMAL:
+      break;
+
+    case STATE_CRUMBLING:
+      if (sprite->animation_done()) {
+        state = STATE_DISINTEGRATING;
+        sprite->set_action("disintegrating", 1);
+        set_group(COLGROUP_DISABLED);
+        physic.enable_gravity(true);
+      }
+      break;
+
+    case STATE_DISINTEGRATING:
+      movement = physic.get_movement(elapsed_time);
+      if (sprite->animation_done()) {
+       remove_me();
+       return;
+      }
+      break;
+  }
+}
+
+IMPLEMENT_FACTORY(UnstableTile, "unstable_tile");
diff --git a/src/object/unstable_tile.hpp b/src/object/unstable_tile.hpp
new file mode 100644 (file)
index 0000000..a6bb71e
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux - Unstable Tile
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __UNSTABLE_TILE_H__
+#define __UNSTABLE_TILE_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+/**
+ * A block that disintegrates when stood on
+ */
+class UnstableTile : public MovingSprite, public UsesPhysic
+{
+public:
+  UnstableTile(const lisp::Lisp& lisp);
+  virtual UnstableTile* clone() const { return new UnstableTile(*this); }
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+
+private:
+  enum State {
+    STATE_NORMAL,        /**< default state */
+    STATE_CRUMBLING,     /**< crumbling, still solid */
+    STATE_DISINTEGRATING /**< disintegrating, no longer solid */
+  };
+  State state;
+};
+
+#endif
diff --git a/src/object/weak_block.cpp b/src/object/weak_block.cpp
new file mode 100644 (file)
index 0000000..182efae
--- /dev/null
@@ -0,0 +1,121 @@
+//  $Id$
+//
+//  SuperTux - Weak Block
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "weak_block.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+#include "object/bullet.hpp"
+
+WeakBlock::WeakBlock(const lisp::Lisp& lisp)
+  : MovingSprite(lisp, "images/objects/strawbox/strawbox.sprite", LAYER_TILES, COLGROUP_STATIC), state(STATE_NORMAL)
+{
+  sprite->set_action("normal");
+}
+
+HitResponse
+WeakBlock::collision(GameObject& other, const CollisionHit& )
+{
+  switch (state) {
+
+    case STATE_NORMAL:
+      if (dynamic_cast<Bullet*>(&other)) {
+        startBurning();
+        return FORCE_MOVE;
+      }
+      return FORCE_MOVE;
+      break;
+
+    case STATE_BURNING:
+      return FORCE_MOVE;
+      break;
+
+    case STATE_DISINTEGRATING:
+      return FORCE_MOVE;
+      break;
+
+  }
+
+  log_debug << "unhandled state" << std::endl;
+  return FORCE_MOVE;
+}
+
+void
+WeakBlock::update(float )
+{
+  switch (state) {
+
+    case STATE_NORMAL:
+      break;
+
+    case STATE_BURNING:
+      if (sprite->animation_done()) {
+        state = STATE_DISINTEGRATING;
+        sprite->set_action("disintegrating", 1);
+        spreadHit();
+        set_group(COLGROUP_DISABLED);
+      }
+      break;
+
+    case STATE_DISINTEGRATING:
+      if (sprite->animation_done()) {
+        remove_me();
+        return;
+      }
+      break;
+
+  }
+}
+
+void
+WeakBlock::startBurning()
+{
+  if (state != STATE_NORMAL) return;
+  state = STATE_BURNING;
+  sprite->set_action("burning", 1);
+}
+
+void
+WeakBlock::spreadHit()
+{
+  Sector* sector = Sector::current();
+  if (!sector) {
+    log_debug << "no current sector" << std::endl;
+    return;
+  }
+  for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) {
+    WeakBlock* wb = dynamic_cast<WeakBlock*>(*i);
+    if (!wb) continue;
+    if (wb == this) continue;
+    if (wb->state != STATE_NORMAL) continue;
+    float dx = fabsf(wb->get_pos().x - this->get_pos().x);
+    float dy = fabsf(wb->get_pos().y - this->get_pos().y);
+    if ((dx <= 32.5) && (dy <= 32.5)) wb->startBurning();
+  }
+}
+
+
+IMPLEMENT_FACTORY(WeakBlock, "weak_block");
diff --git a/src/object/weak_block.hpp b/src/object/weak_block.hpp
new file mode 100644 (file)
index 0000000..01e2464
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  SuperTux - Weak Block
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __WEAK_BLOCK_H__
+#define __WEAK_BLOCK_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+/**
+ * A block that can be destroyed by Bullet hits
+ */
+class WeakBlock : public MovingSprite, public UsesPhysic
+{
+public:
+  WeakBlock(const lisp::Lisp& lisp);
+
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+  void update(float elapsed_time);
+
+protected:
+  /**
+   * called by self when hit by a bullet
+   */
+  void startBurning();
+
+  /**
+   * pass hit to nearby WeakBlock objects
+   */
+  void spreadHit();
+
+private:
+  enum State {
+    STATE_NORMAL, /**< default state */
+    STATE_BURNING, /**< on fire, still solid */
+    STATE_DISINTEGRATING /**< crumbling to dust, no longer solid */
+  };
+  State state;
+};
+
+#endif
diff --git a/src/object/wind.cpp b/src/object/wind.cpp
new file mode 100644 (file)
index 0000000..638ab69
--- /dev/null
@@ -0,0 +1,122 @@
+//  $Id$
+//
+//  SuperTux - Wind
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "wind.hpp"
+#include "video/drawing_context.hpp"
+#include "object/player.hpp"
+#include "object_factory.hpp"
+#include "random_generator.hpp"
+#include "sector.hpp"
+#include "particles.hpp"
+#include "scripting/wind.hpp"
+#include "scripting/squirrel_util.hpp"
+
+Wind::Wind(const lisp::Lisp& reader)
+  : blowing(true), acceleration(100), elapsed_time(0)
+{
+  reader.get("name", name);
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  float w = 32, h = 32;
+  reader.get("width", w);
+  reader.get("height", h);
+  bbox.set_size(w, h);
+
+  reader.get("blowing", blowing);
+
+  float speed_x = 0, speed_y = 0;
+  reader.get("speed-x", speed_x);
+  reader.get("speed-y", speed_y);
+  speed = Vector(speed_x, speed_y);
+
+  reader.get("acceleration", acceleration);
+
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+Wind::update(float elapsed_time)
+{
+  this->elapsed_time = elapsed_time;
+
+  if (!blowing) return;
+
+  // TODO: nicer, configurable particles for wind?
+  if (systemRandom.rand(0, 100) < 20) {
+    // emit a particle
+    Vector ppos = Vector(systemRandom.randf(bbox.p1.x+8, bbox.p2.x-8), systemRandom.randf(bbox.p1.y+8, bbox.p2.y-8));
+    Vector pspeed = Vector(speed.x, speed.y);
+    Sector::current()->add_object(new Particles(ppos, 44, 46, pspeed, Vector(0,0), 1, Color(.4f, .4f, .4f), 3, .1f, LAYER_BACKGROUNDTILES+1));
+  }
+}
+
+void
+Wind::draw(DrawingContext& )
+{
+}
+
+HitResponse
+Wind::collision(GameObject& other, const CollisionHit& )
+{
+  if (!blowing) return ABORT_MOVE;
+
+  Player* player = dynamic_cast<Player*> (&other);
+  if (player) {
+    if (!player->on_ground()) {
+      player->add_velocity(speed * acceleration * elapsed_time, speed);
+    }
+  }
+
+  return ABORT_MOVE;
+}
+
+void
+Wind::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name == "")
+    return;
+
+  Scripting::Wind* interface = new Scripting::Wind(this);
+  expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Wind::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+  if (name == "")
+    return;
+
+  Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Wind::start()
+{
+  blowing = true;
+}
+
+void
+Wind::stop()
+{
+  blowing = false;
+}
+
+IMPLEMENT_FACTORY(Wind, "wind");
diff --git a/src/object/wind.hpp b/src/object/wind.hpp
new file mode 100644 (file)
index 0000000..e52add7
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id$
+//
+//  SuperTux - Wind
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_WIND_H
+#define SUPERTUX_WIND_H
+
+#include <set>
+#include "moving_object.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite.hpp"
+#include "script_interface.hpp"
+
+class Player;
+
+/**
+ * Defines an area that will gently push Players in one direction
+ */
+class Wind : public MovingObject, public ScriptInterface
+{
+public:
+  Wind(const lisp::Lisp& reader);
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  /**
+   * @name Scriptable Methods
+   * @{
+   */
+
+  /**
+   * start blowing
+   */
+  void start();
+
+  /**
+   * stop blowing
+   */
+  void stop();
+
+  /**
+   * @}
+   */
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+private:
+  bool blowing; /**< true if wind is currently switched on */
+  Vector speed;
+  float acceleration;
+
+  float elapsed_time; /**< stores last elapsed_time gotten at update() */
+};
+
+#endif
diff --git a/src/object_factory.cpp b/src/object_factory.cpp
new file mode 100644 (file)
index 0000000..fe89724
--- /dev/null
@@ -0,0 +1,54 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <sstream>
+#include <stdexcept>
+
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "object_factory.hpp"
+#include "math/vector.hpp"
+
+GameObject* create_object(const std::string& name, const lisp::Lisp& reader)
+{
+  Factory::Factories::iterator i = Factory::get_factories().find(name);
+  if(i == Factory::get_factories().end()) {
+    std::stringstream msg;
+    msg << "No factory for object '" << name << "' found.";
+    throw std::runtime_error(msg.str());
+  }
+
+  return i->second->create_object(reader);
+}
+
+GameObject* create_object(const std::string& name, const Vector& pos)
+{
+  std::stringstream lisptext;
+  lisptext << "(" << name
+           << " (x " << pos.x << ")"
+           << " (y " << pos.y << "))";
+
+  lisp::Parser parser;
+  const lisp::Lisp* lisp = parser.parse(lisptext, "create_object");
+  GameObject* object = create_object(name, *lisp);
+
+  return object;
+}
diff --git a/src/object_factory.hpp b/src/object_factory.hpp
new file mode 100644 (file)
index 0000000..0e0d0d3
--- /dev/null
@@ -0,0 +1,79 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef OBJECT_FACTORY_H
+#define OBJECT_FACTORY_H
+
+#include <string>
+#include <map>
+
+namespace lisp { class Lisp; }
+class Vector;
+class GameObject;
+
+class Factory
+{
+public:
+  virtual ~Factory()
+  { }
+
+  /** Creates a new gameobject from a lisp node.
+   * Remember to delete the objects later
+   */
+  virtual GameObject* create_object(const lisp::Lisp& reader) = 0;
+
+  typedef std::map<std::string, Factory*> Factories;
+  static Factories &get_factories()
+  {
+    static Factories object_factories;
+    return object_factories;
+  }
+};
+
+GameObject* create_object(const std::string& name, const lisp::Lisp& reader);
+GameObject* create_object(const std::string& name, const Vector& pos);
+
+/** comment from Matze:
+ * Yes I know macros are evil, but in this specific case they save
+ * A LOT of typing and evil code duplication.
+ * I'll happily accept alternatives if someone can present me one that does
+ * not involve typing 4 or more lines for each object class
+ */
+#define IMPLEMENT_FACTORY(CLASS, NAME)                            \
+class INTERN_##CLASS##Factory : public Factory                    \
+{                                                                 \
+public:                                                           \
+  INTERN_##CLASS##Factory()                                       \
+  {                                                               \
+    get_factories()[NAME] = this;                                \
+  }                                                               \
+                                                                  \
+  ~INTERN_##CLASS##Factory()                                      \
+  {                                                               \
+    get_factories().erase(NAME);                                 \
+  }                                                               \
+                                                                  \
+  virtual GameObject* create_object(const lisp::Lisp& reader)     \
+  {                                                               \
+    return new CLASS(reader);                                     \
+  }                                                               \
+};                                                                \
+static INTERN_##CLASS##Factory factory_##CLASS;
+
+#endif
diff --git a/src/object_remove_listener.hpp b/src/object_remove_listener.hpp
new file mode 100644 (file)
index 0000000..a405466
--- /dev/null
@@ -0,0 +1,35 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __OBJECT_REMOVE_LISTENER_H__
+#define __OBJECT_REMOVE_LISTENER_H__
+
+class GameObject;
+
+class ObjectRemoveListener
+{
+public:
+  virtual ~ObjectRemoveListener()
+  {}
+
+  virtual void object_removed(GameObject* object) = 0;
+};
+
+#endif
diff --git a/src/obstack/obstack.c b/src/obstack/obstack.c
new file mode 100644 (file)
index 0000000..e0cac07
--- /dev/null
@@ -0,0 +1,359 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+   Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "obstack.h"
+
+/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
+   incremented whenever callers compiled using an old obstack.h can no
+   longer properly call the functions in this obstack.c.  */
+#define OBSTACK_INTERFACE_VERSION 1
+
+#include <stdio.h>             /* Random thing to get __GNU_LIBRARY__.  */
+#include <stddef.h>
+#include <stdint.h>
+
+/* Determine default alignment.  */
+union fooround
+{
+  uintmax_t i;
+  long double d;
+  void *p;
+};
+struct fooalign
+{
+  char c;
+  union fooround u;
+};
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+   But in fact it might be less smart and round addresses to as much as
+   DEFAULT_ROUNDING.  So we prepare for it to do that.  */
+enum
+  {
+    DEFAULT_ALIGNMENT = offsetof (struct fooalign, u),
+    DEFAULT_ROUNDING = sizeof (union fooround)
+  };
+
+/* When we copy a long block of data, this is the unit to do it with.
+   On some machines, copying successive ints does not work;
+   in such a case, redefine COPYING_UNIT to `long' (if that works)
+   or `char' as a last resort.  */
+# ifndef COPYING_UNIT
+#  define COPYING_UNIT int
+# endif
+
+
+/* The functions allocating more room by calling `obstack_chunk_alloc'
+   jump to the handler pointed to by `obstack_alloc_failed_handler'.
+   This can be set to a user defined function which should either
+   abort gracefully or use longjump - but shouldn't return.  This
+   variable by default points to the internal function
+   `print_and_abort'.  */
+static void print_and_abort (void);
+void (*obstack_alloc_failed_handler) (void) = print_and_abort;
+
+/* Exit value used when `print_and_abort' is used.  */
+# include <stdlib.h>
+int obstack_exit_failure = EXIT_FAILURE;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+   calling interface, or calls functions with the mmalloc/mfree interface
+   (that adds an extra first argument), based on the state of use_extra_arg.
+   For free, do not use ?:, since some compilers, like the MIPS compilers,
+   do not allow (expr) ? void : void.  */
+
+# define CALL_CHUNKFUN(h, size) \
+  (((h) -> use_extra_arg) \
+   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+
+# define CALL_FREEFUN(h, old_chunk) \
+  do { \
+    if ((h) -> use_extra_arg) \
+      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+    else \
+      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+  } while (0)
+
+\f
+/* Initialize an obstack H for use.  Specify chunk size SIZE (0 means default).
+   Objects start on multiples of ALIGNMENT (0 means use default).
+   CHUNKFUN is the function to use to allocate chunks,
+   and FREEFUN the function to free them.
+
+   Return nonzero if successful, calls obstack_alloc_failed_handler if
+   allocation fails.  */
+
+int
+_obstack_begin (struct obstack *h,
+               int size, int alignment,
+               void *(*chunkfun) (long),
+               void (*freefun) (void *))
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+        Use the values for range checking, because if range checking is off,
+        the extra bytes won't be missed terribly, but if range checking is on
+        and we used a larger request, a whole extra 4096 bytes would be
+        allocated.
+
+        These number are irrelevant to the new GNU malloc.  I suspect it is
+        less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+                   + 4 + DEFAULT_ROUNDING - 1)
+                  & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->use_extra_arg = 0;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+                                              alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = 0;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+int
+_obstack_begin_1 (struct obstack *h, int size, int alignment,
+                 void *(*chunkfun) (void *, long),
+                 void (*freefun) (void *, void *),
+                 void *arg)
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+        Use the values for range checking, because if range checking is off,
+        the extra bytes won't be missed terribly, but if range checking is on
+        and we used a larger request, a whole extra 4096 bytes would be
+        allocated.
+
+        These number are irrelevant to the new GNU malloc.  I suspect it is
+        less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+                   + 4 + DEFAULT_ROUNDING - 1)
+                  & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->extra_arg = arg;
+  h->use_extra_arg = 1;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+                                              alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = 0;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+   on the assumption that LENGTH bytes need to be added
+   to the current object, or a new object of length LENGTH allocated.
+   Copies any partial object from the end of the old chunk
+   to the beginning of the new one.  */
+
+void
+_obstack_newchunk (struct obstack *h, int length)
+{
+  register struct _obstack_chunk *old_chunk = h->chunk;
+  register struct _obstack_chunk *new_chunk;
+  register long        new_size;
+  register long obj_size = h->next_free - h->object_base;
+  register long i;
+  long already;
+  char *object_base;
+
+  /* Compute size for new chunk.  */
+  new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100;
+  if (new_size < h->chunk_size)
+    new_size = h->chunk_size;
+
+  /* Allocate and initialize the new chunk.  */
+  new_chunk = CALL_CHUNKFUN (h, new_size);
+  if (!new_chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->chunk = new_chunk;
+  new_chunk->prev = old_chunk;
+  new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+  /* Compute an aligned object_base in the new chunk */
+  object_base =
+    __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
+
+  /* Move the existing object to the new chunk.
+     Word at a time is fast and is safe if the object
+     is sufficiently aligned.  */
+  if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+    {
+      for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+          i >= 0; i--)
+       ((COPYING_UNIT *)object_base)[i]
+         = ((COPYING_UNIT *)h->object_base)[i];
+      /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+        but that can cross a page boundary on a machine
+        which does not do strict alignment for COPYING_UNITS.  */
+      already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+    }
+  else
+    already = 0;
+  /* Copy remaining bytes one by one.  */
+  for (i = already; i < obj_size; i++)
+    object_base[i] = h->object_base[i];
+
+  /* If the object just copied was the only data in OLD_CHUNK,
+     free that chunk and remove it from the chain.
+     But not if that chunk might contain an empty object.  */
+  if (! h->maybe_empty_object
+      && (h->object_base
+         == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
+                         h->alignment_mask)))
+    {
+      new_chunk->prev = old_chunk->prev;
+      CALL_FREEFUN (h, old_chunk);
+    }
+
+  h->object_base = object_base;
+  h->next_free = h->object_base + obj_size;
+  /* The new chunk certainly contains no empty object yet.  */
+  h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+   This is here for debugging.
+   If you use it in a program, you are probably losing.  */
+
+/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
+   obstack.h because it is just for debugging.  */
+int _obstack_allocated_p (struct obstack *h, void *obj);
+
+int
+_obstack_allocated_p (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;  /* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp; /* point to previous chunk if any */
+
+  lp = (h)->chunk;
+  /* We use >= rather than > since the object cannot be exactly at
+     the beginning of the chunk but might be an empty object exactly
+     at the end of an adjacent chunk.  */
+  while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      lp = plp;
+    }
+  return lp != 0;
+}
+\f
+/* Free objects in obstack H, including OBJ and everything allocate
+   more recently than OBJ.  If OBJ is zero, free everything in H.  */
+
+# undef obstack_free
+
+void
+obstack_free (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;  /* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp; /* point to previous chunk if any */
+
+  lp = h->chunk;
+  /* We use >= because there cannot be an object at the beginning of a chunk.
+     But there can be an empty object at that address
+     at the end of another chunk.  */
+  while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      CALL_FREEFUN (h, lp);
+      lp = plp;
+      /* If we switch chunks, we can't tell whether the new current
+        chunk contains an empty object, so assume that it may.  */
+      h->maybe_empty_object = 1;
+    }
+  if (lp)
+    {
+      h->object_base = h->next_free = (char *) (obj);
+      h->chunk_limit = lp->limit;
+      h->chunk = lp;
+    }
+  else if (obj != 0)
+    /* obj is not in any of the chunks! */
+    abort ();
+}
+
+int
+_obstack_memory_used (struct obstack *h)
+{
+  register struct _obstack_chunk* lp;
+  register int nbytes = 0;
+
+  for (lp = h->chunk; lp != 0; lp = lp->prev)
+    {
+      nbytes += lp->limit - (char *) lp;
+    }
+  return nbytes;
+}
+
+static void
+__attribute__ ((noreturn))
+print_and_abort (void)
+{
+  /* Don't change any of these strings.  Yes, it would be possible to add
+     the newline to the string and use fputs or so.  But this must not
+     happen because the "memory exhausted" message appears in other places
+     like this and the translation should be reused instead of creating
+     a very similar string which requires a separate translation.  */
+  fprintf (stderr, "%s\n", "memory exhausted");
+  exit (obstack_exit_failure);
+}
+
diff --git a/src/obstack/obstack.h b/src/obstack/obstack.h
new file mode 100644 (file)
index 0000000..206fe55
--- /dev/null
@@ -0,0 +1,509 @@
+/* obstack.h - object stack macros
+   Copyright (C) 1988-1994,1996-1999,2003,2004,2005
+       Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects.  Each object starts life
+small, and may grow to maturity.  (Consider building a word syllable
+by syllable.)  An object can move while it is growing.  Once it has
+been "finished" it never changes address again.  So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'.  On occasion, they free chunks,
+by calling `obstack_chunk_free'.  You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables.  Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols.  At the time you are reading a symbol you don't know
+how long it is.  One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer.  This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently.  Use one obstack for all symbol
+names.  As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it.  Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses.  When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk.  When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies.  No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk.  We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object.  This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+       We allocate large chunks.
+       We carve out one object at a time from the current chunk.
+       Once carved, an object never moves.
+       We are free to append data of any size to the currently
+         growing object.
+       Exactly one object is growing in an obstack at any one time.
+       You can run one obstack per control block.
+       You may have as many control blocks as you dare.
+       Because of the way we do it, you can `unwind' an obstack
+         back to a previous state. (You may remove objects much
+         as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once.  */
+
+#ifndef _OBSTACK_H
+#define _OBSTACK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+\f
+/* We need the type of a pointer subtraction.  If __PTRDIFF_TYPE__ is
+   defined, as with GNU C, use that; that way we don't pollute the
+   namespace with <stddef.h>'s symbols.  Otherwise, include <stddef.h>
+   and use ptrdiff_t.  */
+
+#ifdef __PTRDIFF_TYPE__
+# define PTR_INT_TYPE __PTRDIFF_TYPE__
+#else
+# include <stddef.h>
+# define PTR_INT_TYPE ptrdiff_t
+#endif
+
+/* If B is the base of an object addressed by P, return the result of
+   aligning P to the next multiple of A + 1.  B and P must be of type
+   char *.  A + 1 must be a power of 2.  */
+
+#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
+
+/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case
+   where pointers can be converted to integers, aligned as integers,
+   and converted back again.  If PTR_INT_TYPE is narrower than a
+   pointer (e.g., the AS/400), play it safe and compute the alignment
+   relative to B.  Otherwise, use the faster strategy of computing the
+   alignment relative to 0.  */
+
+#define __PTR_ALIGN(B, P, A)                                               \
+  __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \
+               P, A)
+
+#include <string.h>
+
+struct _obstack_chunk          /* Lives at front of each chunk. */
+{
+  char  *limit;                        /* 1 past end of this chunk */
+  struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+  char contents[4];            /* objects begin here */
+};
+
+struct obstack         /* control current object in current chunk */
+{
+  long chunk_size;             /* preferred size to allocate chunks in */
+  struct _obstack_chunk *chunk;        /* address of current struct obstack_chunk */
+  char *object_base;           /* address of object we are building */
+  char *next_free;             /* where to add next char to current object */
+  char *chunk_limit;           /* address of char after current chunk */
+  union
+  {
+    PTR_INT_TYPE tempint;
+    void *tempptr;
+  } temp;                      /* Temporary for some macros.  */
+  int   alignment_mask;                /* Mask of alignment for each object. */
+  /* These prototypes vary based on `use_extra_arg', and we use
+     casts to the prototypeless function type in all assignments,
+     but having prototypes here quiets -Wstrict-prototypes.  */
+  struct _obstack_chunk *(*chunkfun) (void *, long);
+  void (*freefun) (void *, struct _obstack_chunk *);
+  void *extra_arg;             /* first arg for chunk alloc/dealloc funcs */
+  unsigned use_extra_arg:1;    /* chunk alloc/dealloc funcs take extra arg */
+  unsigned maybe_empty_object:1;/* There is a possibility that the current
+                                  chunk contains a zero-length object.  This
+                                  prevents freeing the chunk if we allocate
+                                  a bigger chunk to replace it. */
+  unsigned alloc_failed:1;     /* No longer used, as we now call the failed
+                                  handler on error, but retained for binary
+                                  compatibility.  */
+};
+
+/* Declare the external functions we use; they are in obstack.c.  */
+
+extern void _obstack_newchunk (struct obstack *, int);
+extern int _obstack_begin (struct obstack *, int, int,
+                           void *(*) (long), void (*) (void *));
+extern int _obstack_begin_1 (struct obstack *, int, int,
+                            void *(*) (void *, long),
+                            void (*) (void *, void *), void *);
+extern int _obstack_memory_used (struct obstack *);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+\f
+/* Error handler called when `obstack_chunk_alloc' failed to allocate
+   more memory.  This can be set to a user defined function which
+   should either abort gracefully or use longjump - but shouldn't
+   return.  The default action is to print a message and abort.  */
+extern void (*obstack_alloc_failed_handler) (void);
+
+/* Exit value used when `print_and_abort' is used.  */
+extern int obstack_exit_failure;
+\f
+/* Pointer to beginning of object being allocated or to be allocated next.
+   Note that this might not be the final address of the object
+   because a new chunk might be needed to hold the final size.  */
+
+#define obstack_base(h) ((void *) (h)->object_base)
+
+/* Size for allocating ordinary chunks.  */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk.  */
+
+#define obstack_next_free(h)   ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object.  */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+/* To prevent prototype warnings provide complete argument list.  */
+#define obstack_init(h)                                                \
+  _obstack_begin ((h), 0, 0,                                   \
+                 (void *(*) (long)) obstack_chunk_alloc,       \
+                 (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_begin(h, size)                                 \
+  _obstack_begin ((h), (size), 0,                              \
+                 (void *(*) (long)) obstack_chunk_alloc,       \
+                 (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun)  \
+  _obstack_begin ((h), (size), (alignment),                               \
+                 (void *(*) (long)) (chunkfun),                           \
+                 (void (*) (void *)) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+  _obstack_begin_1 ((h), (size), (alignment),                          \
+                   (void *(*) (void *, long)) (chunkfun),              \
+                   (void (*) (void *, void *)) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#define obstack_memory_used(h) _obstack_memory_used (h)
+\f
+#if defined __GNUC__ && defined __STDC__ && __STDC__
+/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and
+   does not implement __extension__.  But that compiler doesn't define
+   __GNUC_MINOR__.  */
+# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__)
+#  define __extension__
+# endif
+
+/* For GNU C, if not -traditional,
+   we can define these macros to compute all args only once
+   without using a global variable.
+   Also, we can avoid using the `temp' slot, to make faster code.  */
+
+# define obstack_object_size(OBSTACK)                                  \
+  __extension__                                                                \
+  ({ struct obstack const *__o = (OBSTACK);                            \
+     (unsigned) (__o->next_free - __o->object_base); })
+
+# define obstack_room(OBSTACK)                                         \
+  __extension__                                                                \
+  ({ struct obstack const *__o = (OBSTACK);                            \
+     (unsigned) (__o->chunk_limit - __o->next_free); })
+
+# define obstack_make_room(OBSTACK,length)                             \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   int __len = (length);                                               \
+   if (__o->chunk_limit - __o->next_free < __len)                      \
+     _obstack_newchunk (__o, __len);                                   \
+   (void) 0; })
+
+# define obstack_empty_p(OBSTACK)                                      \
+  __extension__                                                                \
+  ({ struct obstack const *__o = (OBSTACK);                            \
+     (__o->chunk->prev == 0                                            \
+      && __o->next_free == __PTR_ALIGN ((char *) __o->chunk,           \
+                                       __o->chunk->contents,           \
+                                       __o->alignment_mask)); })
+
+# define obstack_grow(OBSTACK,where,length)                            \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   int __len = (length);                                               \
+   if (__o->next_free + __len > __o->chunk_limit)                      \
+     _obstack_newchunk (__o, __len);                                   \
+   memcpy (__o->next_free, where, __len);                              \
+   __o->next_free += __len;                                            \
+   (void) 0; })
+
+# define obstack_grow0(OBSTACK,where,length)                           \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   int __len = (length);                                               \
+   if (__o->next_free + __len + 1 > __o->chunk_limit)                  \
+     _obstack_newchunk (__o, __len + 1);                               \
+   memcpy (__o->next_free, where, __len);                              \
+   __o->next_free += __len;                                            \
+   *(__o->next_free)++ = 0;                                            \
+   (void) 0; })
+
+# define obstack_1grow(OBSTACK,datum)                                  \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   if (__o->next_free + 1 > __o->chunk_limit)                          \
+     _obstack_newchunk (__o, 1);                                       \
+   obstack_1grow_fast (__o, datum);                                    \
+   (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers
+   or ints, and that the data added so far to the current object
+   shares that much alignment.  */
+
+# define obstack_ptr_grow(OBSTACK,datum)                               \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   if (__o->next_free + sizeof (void *) > __o->chunk_limit)            \
+     _obstack_newchunk (__o, sizeof (void *));                         \
+   obstack_ptr_grow_fast (__o, datum); })                              \
+
+# define obstack_int_grow(OBSTACK,datum)                               \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   if (__o->next_free + sizeof (int) > __o->chunk_limit)               \
+     _obstack_newchunk (__o, sizeof (int));                            \
+   obstack_int_grow_fast (__o, datum); })
+
+# define obstack_ptr_grow_fast(OBSTACK,aptr)                           \
+__extension__                                                          \
+({ struct obstack *__o1 = (OBSTACK);                                   \
+   *(const void **) __o1->next_free = (aptr);                          \
+   __o1->next_free += sizeof (const void *);                           \
+   (void) 0; })
+
+# define obstack_int_grow_fast(OBSTACK,aint)                           \
+__extension__                                                          \
+({ struct obstack *__o1 = (OBSTACK);                                   \
+   *(int *) __o1->next_free = (aint);                                  \
+   __o1->next_free += sizeof (int);                                    \
+   (void) 0; })
+
+# define obstack_blank(OBSTACK,length)                                 \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   int __len = (length);                                               \
+   if (__o->chunk_limit - __o->next_free < __len)                      \
+     _obstack_newchunk (__o, __len);                                   \
+   obstack_blank_fast (__o, __len);                                    \
+   (void) 0; })
+
+# define obstack_alloc(OBSTACK,length)                                 \
+__extension__                                                          \
+({ struct obstack *__h = (OBSTACK);                                    \
+   obstack_blank (__h, (length));                                      \
+   obstack_finish (__h); })
+
+# define obstack_copy(OBSTACK,where,length)                            \
+__extension__                                                          \
+({ struct obstack *__h = (OBSTACK);                                    \
+   obstack_grow (__h, (where), (length));                              \
+   obstack_finish (__h); })
+
+# define obstack_copy0(OBSTACK,where,length)                           \
+__extension__                                                          \
+({ struct obstack *__h = (OBSTACK);                                    \
+   obstack_grow0 (__h, (where), (length));                             \
+   obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+   when obstack_blank is called.  */
+# define obstack_finish(OBSTACK)                                       \
+__extension__                                                          \
+({ struct obstack *__o1 = (OBSTACK);                                   \
+   void *__value = (void *) __o1->object_base;                         \
+   if (__o1->next_free == __value)                                     \
+     __o1->maybe_empty_object = 1;                                     \
+   __o1->next_free                                                     \
+     = __PTR_ALIGN (__o1->object_base, __o1->next_free,                        \
+                   __o1->alignment_mask);                              \
+   if (__o1->next_free - (char *)__o1->chunk                           \
+       > __o1->chunk_limit - (char *)__o1->chunk)                      \
+     __o1->next_free = __o1->chunk_limit;                              \
+   __o1->object_base = __o1->next_free;                                        \
+   __value; })
+
+# define obstack_free(OBSTACK, OBJ)                                    \
+__extension__                                                          \
+({ struct obstack *__o = (OBSTACK);                                    \
+   void *__obj = (OBJ);                                                        \
+   if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit)  \
+     __o->next_free = __o->object_base = (char *)__obj;                        \
+   else (obstack_free) (__o, __obj); })
+\f
+#else /* not __GNUC__ or not __STDC__ */
+
+# define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+# define obstack_room(h)               \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+# define obstack_empty_p(h) \
+ ((h)->chunk->prev == 0                                                        \
+  && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk,               \
+                                   (h)->chunk->contents,               \
+                                   (h)->alignment_mask))
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+   so that we can avoid having void expressions
+   in the arms of the conditional expression.
+   Casting the third operand to void was tried before,
+   but some compilers won't accept it.  */
+
+# define obstack_make_room(h,length)                                   \
+( (h)->temp.tempint = (length),                                                \
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)             \
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0))
+
+# define obstack_grow(h,where,length)                                  \
+( (h)->temp.tempint = (length),                                                \
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)             \
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),             \
+  memcpy ((h)->next_free, where, (h)->temp.tempint),                   \
+  (h)->next_free += (h)->temp.tempint)
+
+# define obstack_grow0(h,where,length)                                 \
+( (h)->temp.tempint = (length),                                                \
+  (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit)         \
+   ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0),         \
+  memcpy ((h)->next_free, where, (h)->temp.tempint),                   \
+  (h)->next_free += (h)->temp.tempint,                                 \
+  *((h)->next_free)++ = 0)
+
+# define obstack_1grow(h,datum)                                                \
+( (((h)->next_free + 1 > (h)->chunk_limit)                             \
+   ? (_obstack_newchunk ((h), 1), 0) : 0),                             \
+  obstack_1grow_fast (h, datum))
+
+# define obstack_ptr_grow(h,datum)                                     \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit)               \
+   ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0),               \
+  obstack_ptr_grow_fast (h, datum))
+
+# define obstack_int_grow(h,datum)                                     \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit)                  \
+   ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0),                  \
+  obstack_int_grow_fast (h, datum))
+
+# define obstack_ptr_grow_fast(h,aptr)                                 \
+  (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr))
+
+# define obstack_int_grow_fast(h,aint)                                 \
+  (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint))
+
+# define obstack_blank(h,length)                                       \
+( (h)->temp.tempint = (length),                                                \
+  (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint)             \
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),             \
+  obstack_blank_fast (h, (h)->temp.tempint))
+
+# define obstack_alloc(h,length)                                       \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+# define obstack_copy(h,where,length)                                  \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_copy0(h,where,length)                                 \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_finish(h)                                             \
+( ((h)->next_free == (h)->object_base                                  \
+   ? (((h)->maybe_empty_object = 1), 0)                                        \
+   : 0),                                                               \
+  (h)->temp.tempptr = (h)->object_base,                                        \
+  (h)->next_free                                                       \
+    = __PTR_ALIGN ((h)->object_base, (h)->next_free,                   \
+                  (h)->alignment_mask),                                \
+  (((h)->next_free - (char *) (h)->chunk                               \
+    > (h)->chunk_limit - (char *) (h)->chunk)                          \
+   ? ((h)->next_free = (h)->chunk_limit) : 0),                         \
+  (h)->object_base = (h)->next_free,                                   \
+  (h)->temp.tempptr)
+
+# define obstack_free(h,obj)                                           \
+( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk,            \
+  ((((h)->temp.tempint > 0                                             \
+    && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk))    \
+   ? (int) ((h)->next_free = (h)->object_base                          \
+           = (h)->temp.tempint + (char *) (h)->chunk)                  \
+   : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#ifdef __cplusplus
+}      /* C++ */
+#endif
+
+#endif /* obstack.h */
diff --git a/src/obstack/obstackpp.hpp b/src/obstack/obstackpp.hpp
new file mode 100644 (file)
index 0000000..6c345f5
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef OBSTACKPP_H_
+#define OBSTACKPP_H_
+
+#include "obstack.h"
+
+inline void*
+operator new (size_t bytes, struct obstack& obst)
+{
+  return obstack_alloc(&obst, bytes);
+}
+
+inline void*
+operator new[] (size_t bytes, struct obstack& obst)
+{
+  return obstack_alloc(&obst, bytes);
+}
+
+static inline void* obstack_chunk_alloc(size_t size)
+{
+  return new char[size];
+}
+
+static inline void obstack_chunk_free(void* data)
+{
+  char* ptr = static_cast<char*> (data);
+  delete[] ptr;
+}
+
+#endif
+
diff --git a/src/options_menu.cpp b/src/options_menu.cpp
new file mode 100644 (file)
index 0000000..93ab43d
--- /dev/null
@@ -0,0 +1,170 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobas Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "options_menu.hpp"
+#include "gui/menu.hpp"
+#include "audio/sound_manager.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "main.hpp"
+#include "gettext.hpp"
+#include "gameconfig.hpp"
+
+Menu* options_menu   = 0;
+
+enum OptionsMenuIDs {
+  MNID_FULLSCREEN,
+  MNID_SOUND,
+  MNID_MUSIC
+};
+
+class LanguageMenu : public Menu
+{
+public:
+  LanguageMenu() {
+    add_label(_("Language"));
+    add_hl();
+    add_entry(0, std::string("(")+_("auto-detect language")+")");
+    add_entry(1, "English");
+
+    int mnid = 10;    
+    std::set<std::string> languages = dictionary_manager.get_languages();
+    for (std::set<std::string>::iterator i = languages.begin(); i != languages.end(); i++) {
+      std::string locale_name = *i;
+      TinyGetText::LanguageDef ldef = TinyGetText::get_language_def(locale_name);
+      std::string locale_fullname = locale_name;
+      if (std::string(ldef.code) == locale_name) {
+        locale_fullname = ldef.name;
+      }
+      add_entry(mnid++, locale_fullname);
+    } 
+
+    add_hl();
+    add_back(_("Back"));
+  }
+
+  virtual void menu_action(MenuItem* item) {
+    if (item->id == 0) {
+      config->locale = "";
+      dictionary_manager.set_language(config->locale);
+      config->save();
+      Menu::set_current(0);
+    }
+    else if (item->id == 1) {
+      config->locale = "en";
+      dictionary_manager.set_language(config->locale);
+      config->save();
+      Menu::set_current(0);
+    }
+    int mnid = 10;    
+    std::set<std::string> languages = dictionary_manager.get_languages();
+    for (std::set<std::string>::iterator i = languages.begin(); i != languages.end(); i++) {
+      std::string locale_name = *i;
+      if (item->id == mnid++) {
+        config->locale = locale_name;
+        dictionary_manager.set_language(config->locale);
+        config->save();
+        Menu::set_current(0);
+      }
+    }
+  }
+};
+
+
+class OptionsMenu : public Menu
+{
+public:
+  OptionsMenu();
+  virtual ~OptionsMenu();
+
+  virtual void menu_action(MenuItem* item);
+
+protected:
+  std::auto_ptr<LanguageMenu> language_menu;
+  
+};
+
+OptionsMenu::OptionsMenu()
+{
+  language_menu.reset(new LanguageMenu());
+
+  add_label(_("Options"));
+  add_hl();
+  add_toggle(MNID_FULLSCREEN,_("Fullscreen"), config->use_fullscreen);
+  add_submenu(_("Language"), language_menu.get());
+  if (sound_manager->is_audio_enabled()) {
+    add_toggle(MNID_SOUND, _("Sound"), config->sound_enabled);
+    add_toggle(MNID_MUSIC, _("Music"), config->music_enabled);
+  } else {
+    add_deactive(MNID_SOUND, _("Sound (disabled)"));
+    add_deactive(MNID_SOUND, _("Music (disabled)"));
+  }
+  add_submenu(_("Setup Keyboard"), main_controller->get_key_options_menu());
+  add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu());
+  add_hl();
+  add_back(_("Back"));
+}
+
+OptionsMenu::~OptionsMenu()
+{
+}
+
+void
+OptionsMenu::menu_action(MenuItem* item)
+{
+  switch (item->id) {
+    case MNID_FULLSCREEN:
+      if(config->use_fullscreen != options_menu->is_toggled(MNID_FULLSCREEN)) {
+        config->use_fullscreen = !config->use_fullscreen;
+        init_video();
+        config->save();
+      }
+      break;
+    case MNID_SOUND:
+      if(config->sound_enabled != options_menu->is_toggled(MNID_SOUND)) {
+        config->sound_enabled = !config->sound_enabled;
+        sound_manager->enable_sound(config->sound_enabled);
+        config->save();
+      }
+      break;
+    case MNID_MUSIC:
+      if(config->music_enabled != options_menu->is_toggled(MNID_MUSIC)) {
+        config->music_enabled = !config->music_enabled;
+        sound_manager->enable_music(config->music_enabled);
+        config->save();
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+Menu* get_options_menu()
+{
+  //static OptionsMenu menu;
+  options_menu = new OptionsMenu();
+  return options_menu;
+}
+
+void free_options_menu()
+{
+  delete options_menu;
+  options_menu = 0;
+}
diff --git a/src/options_menu.hpp b/src/options_menu.hpp
new file mode 100644 (file)
index 0000000..8f92fe4
--- /dev/null
@@ -0,0 +1,27 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobas Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __OPTIONS_MENU_HPP__
+#define __OPTIONS_MENU_HPP__
+
+class Menu;
+Menu* get_options_menu();
+void free_options_menu();
+
+#endif
diff --git a/src/physfs/physfs_sdl.cpp b/src/physfs/physfs_sdl.cpp
new file mode 100644 (file)
index 0000000..4ad2b6e
--- /dev/null
@@ -0,0 +1,102 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "physfs_sdl.hpp"
+
+#include <physfs.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+
+#include <assert.h>
+#include "log.hpp"
+
+static int funcSeek(struct SDL_RWops* context, int offset, int whence)
+{
+    PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+    int res;
+    switch(whence) {
+        case SEEK_SET:
+            res = PHYSFS_seek(file, offset);
+            break;
+        case SEEK_CUR:
+            res = PHYSFS_seek(file, PHYSFS_tell(file) + offset);
+            break;
+        case SEEK_END:
+            res = PHYSFS_seek(file, PHYSFS_fileLength(file) + offset);
+            break;
+        default:
+            res = 0;
+            assert(false);
+            break;
+    }
+    if(res == 0) {
+        log_warning << "Error seeking in file: " << PHYSFS_getLastError() << std::endl;
+        return -1;
+    }
+
+    return (int) PHYSFS_tell(file);
+}
+
+static int funcRead(struct SDL_RWops* context, void* ptr, int size, int maxnum)
+{
+    PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+
+    int res = PHYSFS_read(file, ptr, size, maxnum);
+    return res;
+}
+
+static int funcClose(struct SDL_RWops* context)
+{
+    PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+
+    PHYSFS_close(file);
+    delete context;
+
+    return 0;
+}
+
+SDL_RWops* get_physfs_SDLRWops(const std::string& filename)
+{
+       // check this as PHYSFS seems to be buggy and still returns a
+       // valid pointer in this case
+       if(filename == "") {
+               throw std::runtime_error("Couldn't open file: empty filename");
+       }
+
+    PHYSFS_file* file = (PHYSFS_file*) PHYSFS_openRead(filename.c_str());
+    if(!file) {
+        std::stringstream msg;
+        msg << "Couldn't open '" << filename << "': "
+            << PHYSFS_getLastError();
+        throw std::runtime_error(msg.str());
+    }
+
+    SDL_RWops* ops = new SDL_RWops();
+    ops->type = 0;
+    ops->hidden.unknown.data1 = file;
+    ops->seek = funcSeek;
+    ops->read = funcRead;
+    ops->write = 0;
+    ops->close = funcClose;
+    return ops;
+}
diff --git a/src/physfs/physfs_sdl.hpp b/src/physfs/physfs_sdl.hpp
new file mode 100644 (file)
index 0000000..214d0b1
--- /dev/null
@@ -0,0 +1,28 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PHYSFSSDL_HPP__
+#define __PHYSFSSDL_HPP__
+
+#include <SDL.h>
+#include <string>
+
+SDL_RWops* get_physfs_SDLRWops(const std::string& filename);
+
+#endif
diff --git a/src/physfs/physfs_stream.cpp b/src/physfs/physfs_stream.cpp
new file mode 100644 (file)
index 0000000..c4347b2
--- /dev/null
@@ -0,0 +1,179 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "physfs_stream.hpp"
+
+#include <assert.h>
+#include <physfs.h>
+#include <stdexcept>
+#include <sstream>
+
+IFileStreambuf::IFileStreambuf(const std::string& filename)
+{
+       // check this as PHYSFS seems to be buggy and still returns a
+       // valid pointer in this case
+       if(filename == "") {
+               throw std::runtime_error("Couldn't open file: empty filename");
+       }
+    file = PHYSFS_openRead(filename.c_str());
+    if(file == 0) {
+        std::stringstream msg;
+        msg << "Couldn't open file '" << filename << "': "
+            << PHYSFS_getLastError();
+        throw std::runtime_error(msg.str());
+    }
+}
+
+IFileStreambuf::~IFileStreambuf()
+{
+    PHYSFS_close(file);
+}
+
+int
+IFileStreambuf::underflow()
+{
+    if(PHYSFS_eof(file)) {
+        return traits_type::eof();
+    }
+
+    PHYSFS_sint64 bytesread = PHYSFS_read(file, buf, 1, sizeof(buf));
+    if(bytesread <= 0) {
+        return traits_type::eof();
+    }
+    setg(buf, buf, buf + bytesread);
+
+    return buf[0];
+}
+
+IFileStreambuf::pos_type
+IFileStreambuf::seekpos(pos_type pos, std::ios_base::openmode)
+{
+  if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (pos)) == 0) {
+    return pos_type(off_type(-1));
+  }
+
+  // the seek invalidated the buffer
+  setg(buf, buf, buf);
+  return pos;
+}
+
+IFileStreambuf::pos_type
+IFileStreambuf::seekoff(off_type off, std::ios_base::seekdir dir,
+                        std::ios_base::openmode mode)
+{
+  off_type pos = off;
+  PHYSFS_sint64 ptell = PHYSFS_tell(file);
+
+  switch(dir) {
+    case std::ios_base::beg:
+      break;
+    case std::ios_base::cur:
+      if(off == 0)
+        return static_cast<pos_type> (ptell) - static_cast<pos_type> (egptr() - gptr());
+      pos += static_cast<off_type> (ptell) - static_cast<off_type> (egptr() - gptr());
+      break;
+    case std::ios_base::end:
+      pos += static_cast<off_type> (PHYSFS_fileLength(file));
+      break;
+    default:
+#ifdef DEBUG
+      assert(false);
+#else
+      return pos_type(off_type(-1));
+#endif
+  }
+
+  return seekpos(static_cast<pos_type> (pos), mode);
+}
+
+//---------------------------------------------------------------------------
+
+OFileStreambuf::OFileStreambuf(const std::string& filename)
+{
+    file = PHYSFS_openWrite(filename.c_str());
+    if(file == 0) {
+        std::stringstream msg;
+        msg << "Couldn't open file '" << filename << "': "
+            << PHYSFS_getLastError();
+        throw std::runtime_error(msg.str());
+    }
+
+    setp(buf, buf+sizeof(buf));
+}
+
+OFileStreambuf::~OFileStreambuf()
+{
+    sync();
+    PHYSFS_close(file);
+}
+
+int
+OFileStreambuf::overflow(int c)
+{
+    char c2 = (char)c;
+
+    if(pbase() == pptr())
+        return 0;
+
+    size_t size = pptr() - pbase();
+    PHYSFS_sint64 res = PHYSFS_write(file, pbase(), 1, size);
+    if(res <= 0)
+        return traits_type::eof();
+
+    if(c != traits_type::eof()) {
+        PHYSFS_sint64 res = PHYSFS_write(file, &c2, 1, 1);
+        if(res <= 0)
+            return traits_type::eof();
+    }
+
+    setp(buf, buf + res);
+    return 0;
+}
+
+int
+OFileStreambuf::sync()
+{
+    return overflow(traits_type::eof());
+}
+
+//---------------------------------------------------------------------------
+
+IFileStream::IFileStream(const std::string& filename)
+    : std::istream(new IFileStreambuf(filename))
+{
+}
+
+IFileStream::~IFileStream()
+{
+    delete rdbuf();
+}
+
+//---------------------------------------------------------------------------
+
+OFileStream::OFileStream(const std::string& filename)
+    : std::ostream(new OFileStreambuf(filename))
+{
+}
+
+OFileStream::~OFileStream()
+{
+    delete rdbuf();
+}
diff --git a/src/physfs/physfs_stream.hpp b/src/physfs/physfs_stream.hpp
new file mode 100644 (file)
index 0000000..077a876
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __PHYSFSSTREAM_HPP__
+#define __PHYSFSSTREAM_HPP__
+
+#include <stddef.h>
+#include <physfs.h>
+#include <string>
+#include <streambuf>
+#include <iostream>
+
+/** This class implements a C++ streambuf object for physfs files.
+ * So that you can use normal istream operations on them
+ */
+class IFileStreambuf : public std::streambuf
+{
+public:
+    IFileStreambuf(const std::string& filename);
+    ~IFileStreambuf();
+
+protected:
+    virtual int underflow();
+    virtual pos_type seekoff(off_type pos, std::ios_base::seekdir,
+        std::ios_base::openmode);
+    virtual pos_type seekpos(pos_type pos, std::ios_base::openmode);
+
+private:
+    PHYSFS_file* file;
+    char buf[1024];
+};
+
+class OFileStreambuf : public std::streambuf
+{
+public:
+    OFileStreambuf(const std::string& filename);
+    ~OFileStreambuf();
+
+protected:
+    virtual int overflow(int c);
+    virtual int sync();
+
+private:
+    PHYSFS_file* file;
+    char buf[1024];
+};
+
+class IFileStream : public std::istream
+{
+public:
+    IFileStream(const std::string& filename);
+    ~IFileStream();
+};
+
+class OFileStream : public std::ostream
+{
+public:
+    OFileStream(const std::string& filename);
+    ~OFileStream();
+};
+
+#endif
diff --git a/src/physic.cpp b/src/physic.cpp
new file mode 100644 (file)
index 0000000..5da55a3
--- /dev/null
@@ -0,0 +1,169 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "physic.hpp"
+
+Physic::Physic()
+    : ax(0), ay(0), vx(0), vy(0), gravity_enabled_flag(true), gravity(10 * 100)
+{
+}
+
+Physic::~Physic()
+{
+}
+
+void
+Physic::reset()
+{
+    ax = ay = vx = vy = 0;
+    gravity_enabled_flag = true;
+}
+
+void
+Physic::set_velocity_x(float nvx)
+{
+  vx = nvx;
+}
+
+void
+Physic::set_velocity_y(float nvy)
+{
+  vy = nvy;
+}
+
+void
+Physic::set_velocity(float nvx, float nvy)
+{
+  vx = nvx;
+  vy = nvy;
+}
+
+void
+Physic::set_velocity(const Vector& vector)
+{
+  vx = vector.x;
+  vy = vector.y;
+}
+
+void Physic::inverse_velocity_x()
+{
+  vx = -vx;
+}
+
+void Physic::inverse_velocity_y()
+{
+  vy = -vy;
+}
+
+float
+Physic::get_velocity_x() const
+{
+    return vx;
+}
+
+float
+Physic::get_velocity_y() const
+{
+    return vy;
+}
+
+Vector
+Physic::get_velocity() const
+{
+  return Vector(vx, vy);
+}
+
+void
+Physic::set_acceleration_x(float nax)
+{
+  ax = nax;
+}
+
+void
+Physic::set_acceleration_y(float nay)
+{
+  ay = nay;
+}
+
+void
+Physic::set_acceleration(float nax, float nay)
+{
+  ax = nax;
+  ay = nay;
+}
+
+float
+Physic::get_acceleration_x() const
+{
+  return ax;
+}
+
+float
+Physic::get_acceleration_y() const
+{
+  return ay;
+}
+
+Vector
+Physic::get_acceleration() const
+{
+  return Vector(ax, ay);
+}
+
+void
+Physic::enable_gravity(bool enable_gravity)
+{
+  gravity_enabled_flag = enable_gravity;
+}
+
+bool
+Physic::gravity_enabled() const
+{
+  return gravity_enabled_flag;
+}
+
+void
+Physic::set_gravity(float gravity)
+{
+  this->gravity = gravity * 100;
+}
+
+float
+Physic::get_gravity() const
+{
+  return gravity / 100;
+}
+
+Vector
+Physic::get_movement(float elapsed_time)
+{
+  float grav = gravity_enabled_flag ? gravity : 0;
+
+  Vector result(
+      vx * elapsed_time + ax * elapsed_time * elapsed_time,
+      vy * elapsed_time + (ay + grav) * elapsed_time * elapsed_time
+  );
+  vx += ax * elapsed_time;
+  vy += (ay + grav) * elapsed_time;
+
+  return result;
+}
diff --git a/src/physic.hpp b/src/physic.hpp
new file mode 100644 (file)
index 0000000..3922180
--- /dev/null
@@ -0,0 +1,97 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef SUPERTUX_PHYSIC_H
+#define SUPERTUX_PHYSIC_H
+
+#include "math/vector.hpp"
+
+/// Physics engine.
+/** This is a very simplistic physics engine handling accelerated and constant
+  * movement along with gravity.
+  */
+class Physic
+{
+public:
+  Physic();
+  ~Physic();
+
+  /// Resets all velocities and accelerations to 0.
+  void reset();
+
+  /// Sets velocity to a fixed value.
+  void set_velocity(float vx, float vy);
+  void set_velocity(const Vector& vector);
+
+  void set_velocity_x(float vx);
+  void set_velocity_y(float vy);
+
+  /// Velocities invertion.
+  void inverse_velocity_x();
+  void inverse_velocity_y();
+
+  Vector get_velocity() const;
+  float get_velocity_x() const;
+  float get_velocity_y() const;
+
+  /// Set acceleration.
+  /** Sets acceleration applied to the object. (Note that gravity is
+   * eventually added to the vertical acceleration)
+   */
+  void set_acceleration(float ax, float ay);
+
+  void set_acceleration_x(float ax);
+  void set_acceleration_y(float ay);
+
+  Vector get_acceleration() const;
+  float get_acceleration_x() const;
+  float get_acceleration_y() const;
+
+  /// Enables or disables handling of gravity.
+  void enable_gravity(bool gravity_enabled);
+  bool gravity_enabled() const;
+
+  /// Set gravity to apply to object when enabled
+  void set_gravity(float gravity);
+
+  /// Get gravity to apply to object when enabled
+  float get_gravity() const;
+
+  Vector get_movement(float elapsed_time);
+
+private:
+  /// horizontal and vertical acceleration
+  float ax, ay;
+  /// horizontal and vertical velocity
+  float vx, vy;
+  /// should we respect gravity in our calculations?
+  bool gravity_enabled_flag;
+  /// current gravity (multiplied by 100) to apply to object, if enabled
+  float gravity;
+};
+
+class UsesPhysic
+{
+public:
+  Physic physic;
+  friend class Sector;
+};
+
+#endif
diff --git a/src/player_status.cpp b/src/player_status.cpp
new file mode 100644 (file)
index 0000000..72111e6
--- /dev/null
@@ -0,0 +1,174 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <math.h>
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "player_status.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "math/vector.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+static const int START_COINS = 100;
+static const int MAX_COINS = 99999;
+
+PlayerStatus* player_status = 0;
+
+PlayerStatus::PlayerStatus()
+  : coins(START_COINS),
+    bonus(NO_BONUS),
+    max_fire_bullets(0),
+    max_ice_bullets(0),
+    score_multiplier(1),
+    max_score_multiplier(1)
+{
+  reset();
+
+  coin_surface.reset(new Surface("images/engine/hud/coins-0.png"));
+}
+
+PlayerStatus::~PlayerStatus()
+{
+}
+
+void PlayerStatus::reset()
+{
+  coins = START_COINS;
+  bonus = NO_BONUS;
+  score_multiplier = 1;
+  max_score_multiplier = 1;
+}
+
+void
+PlayerStatus::add_coins(int count, bool play_sound)
+{
+  static float sound_played_time = 0;
+  coins = std::min(coins + count, MAX_COINS);
+  if(play_sound) {
+    if(count >= 100)
+      sound_manager->play("sounds/lifeup.wav");
+    else if (real_time > sound_played_time + 0.010) {
+      sound_manager->play("sounds/coin.wav");
+      sound_played_time = real_time;
+    }
+  }
+}
+
+void
+PlayerStatus::write(lisp::Writer& writer)
+{
+  switch(bonus) {
+    case NO_BONUS:
+      writer.write_string("bonus", "none");
+      break;
+    case GROWUP_BONUS:
+      writer.write_string("bonus", "growup");
+      break;
+    case FIRE_BONUS:
+      writer.write_string("bonus", "fireflower");
+      break;
+    case ICE_BONUS:
+      writer.write_string("bonus", "iceflower");
+      break;
+    default:
+      log_warning << "Unknown bonus type." << std::endl;
+      writer.write_string("bonus", "none");
+  }
+  writer.write_int("fireflowers", max_fire_bullets);
+  writer.write_int("iceflowers", max_ice_bullets);
+
+  writer.write_int("coins", coins);
+  writer.write_int("max-score-multiplier", max_score_multiplier);
+}
+
+void
+PlayerStatus::read(const lisp::Lisp& lisp)
+{
+  reset();
+
+  std::string bonusname;
+  if(lisp.get("bonus", bonusname)) {
+    if(bonusname == "none") {
+      bonus = NO_BONUS;
+    } else if(bonusname == "growup") {
+      bonus = GROWUP_BONUS;
+    } else if(bonusname == "fireflower") {
+      bonus = FIRE_BONUS;
+    } else if(bonusname == "iceflower") {
+      bonus = ICE_BONUS;
+    } else {
+      log_warning << "Unknown bonus '" << bonusname << "' in savefile" << std::endl;
+      bonus = NO_BONUS;
+    }
+  }
+  lisp.get("fireflowers", max_fire_bullets);
+  lisp.get("iceflowers", max_ice_bullets);
+
+  lisp.get("coins", coins);
+  lisp.get("max-score-multiplier", max_score_multiplier);
+}
+
+void
+PlayerStatus::draw(DrawingContext& context)
+{
+  static int displayed_coins = -1;
+  static int next_count = 0;
+
+  if ((displayed_coins == -1) || (fabsf(displayed_coins - coins) > 100)) {
+    displayed_coins = coins;
+  }
+  if (++next_count > 2) {
+    next_count = 0;
+    if (displayed_coins < coins) displayed_coins++;
+    if (displayed_coins > coins) displayed_coins--;
+  }
+  displayed_coins = std::min(std::max(displayed_coins, 0), 9999);
+
+  std::stringstream ss;
+  ss << displayed_coins;
+  std::string coins_text = ss.str();
+
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  Surface* coin_surf = coin_surface.get();
+  if (coin_surf) {
+    context.draw_surface(coin_surf, Vector(SCREEN_WIDTH - BORDER_X - coin_surf->get_width() - gold_fixed_text->get_text_width(coins_text), BORDER_Y + 1), LAYER_HUD);
+  }
+  context.draw_text(gold_fixed_text, coins_text, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y), ALIGN_RIGHT, LAYER_HUD);
+
+  context.pop_transform();
+}
+
+void
+PlayerStatus::operator= (const PlayerStatus& other)
+{
+  coins = other.coins;
+  bonus = other.bonus;
+  score_multiplier = other.score_multiplier;
+  max_score_multiplier = other.max_score_multiplier;
+}
diff --git a/src/player_status.hpp b/src/player_status.hpp
new file mode 100644 (file)
index 0000000..85ac059
--- /dev/null
@@ -0,0 +1,75 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_PLAYERSTATUS_H
+#define SUPERTUX_PLAYERSTATUS_H
+
+#include <memory>
+#include "serializable.hpp"
+
+namespace lisp{ class Writer; }
+namespace lisp{ class Lisp; }
+class Surface;
+
+static const float BORDER_X = 10;
+static const float BORDER_Y = 10;
+
+enum BonusType {
+  NO_BONUS, GROWUP_BONUS, FIRE_BONUS, ICE_BONUS
+};
+class DrawingContext;
+
+/**
+ * This class memorizes player status between different game sessions (for
+ * example when switching maps in the worldmap)
+ */
+class PlayerStatus : public Serializable
+{
+public:
+  PlayerStatus();
+  ~PlayerStatus();
+  void reset();
+  void add_coins(int count, bool play_sound = true);
+
+  void write(lisp::Writer& writer);
+  void read(const lisp::Lisp& lisp);
+
+  void draw(DrawingContext& context);
+
+  int  coins;
+  BonusType bonus;
+  int max_fire_bullets; /**< maximum number of fire bullets in play */
+  int max_ice_bullets; /**< maximum number of ice bullets in play */
+
+  int score_multiplier;
+  int max_score_multiplier;
+
+  void operator= (const PlayerStatus& other);
+
+private:
+  // don't use this
+  PlayerStatus(const PlayerStatus& other);
+
+  std::auto_ptr<Surface> coin_surface;
+};
+
+// global player state
+extern PlayerStatus* player_status;
+
+#endif
diff --git a/src/random_generator.cpp b/src/random_generator.cpp
new file mode 100644 (file)
index 0000000..90fbd5c
--- /dev/null
@@ -0,0 +1,584 @@
+// $Id$
+//
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. 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.
+// 3. Neither the name of the project 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.
+
+// Transliterated into C++ Allen King 060417, from sources on
+//          http://www.jbox.dk/sanos/source/lib/random.c.html
+#include <config.h>
+
+
+#include <stdexcept>
+#include <time.h>
+#include <cassert>
+#include "random_generator.hpp"
+
+RandomGenerator systemRandom;               // global random number generator
+
+RandomGenerator::RandomGenerator() {
+    assert(sizeof(int) >= 4);
+    initialized = 0;
+    debug = 0;                              // change this by hand for debug
+    initialize();
+}
+
+RandomGenerator::~RandomGenerator() {
+}
+
+int RandomGenerator::srand(int x)    {
+    int x0 = x;
+    while (x <= 0)                          // random seed of zero means
+        x = time(0) % RandomGenerator::rand_max; // randomize with time
+
+    if (debug > 0)
+        printf("==== srand(%10d) (%10d) rand_max=%x =====\n",
+               x, x0, RandomGenerator::rand_max);
+
+    RandomGenerator::srandom(x);
+    return x;                               // let caller know seed used
+}
+
+int RandomGenerator::rand() {
+    int rv;                                  // a posative int
+    while ((rv = RandomGenerator::random()) <= 0) // neg or zero causes probs
+        ;
+    if (debug > 0)
+        printf("==== rand(): %10d =====\n", rv);
+    return rv;
+}
+
+int RandomGenerator::rand(int v) {
+    assert(v >= 0 && v <= RandomGenerator::rand_max); // illegal arg
+
+     // remove biases, esp. when v is large (e.g. v == (rand_max/4)*3;)
+    int rv, maxV =(RandomGenerator::rand_max / v) * v;
+    assert(maxV <= RandomGenerator::rand_max);
+    while ((rv = RandomGenerator::random()) >= maxV)
+        ;
+    return rv % v;                          // mod it down to 0..(maxV-1)
+}
+
+int RandomGenerator::rand(int u, int v) {
+    assert(v > u);
+    return u + RandomGenerator::rand(v-u);
+}
+
+double RandomGenerator::randf(double v) {
+    float rv;
+    do {
+               rv = ((double)RandomGenerator::random())/RandomGenerator::rand_max * v;
+       } while (rv >= v);                      // rounding might cause rv==v
+
+    if (debug > 0)
+        printf("==== rand(): %f =====\n", rv);
+    return rv;
+}
+
+double RandomGenerator::randf(double u, double v) {
+    return u + RandomGenerator::randf(v-u);
+}
+
+//-----------------------------------------------------------------------
+//
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. 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.
+// 3. Neither the name of the project 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 <os.h>
+
+//
+// An improved random number generation package.  In addition to the standard
+// rand()/srand() like interface, this package also has a special state info
+// interface.  The initstate() routine is called with a seed, an array of
+// bytes, and a count of how many bytes are being passed in; this array is
+// then initialized to contain information for random number generation with
+// that much state information.  Good sizes for the amount of state
+// information are 32, 64, 128, and 256 bytes.  The state can be switched by
+// calling the setstate() routine with the same array as was initiallized
+// with initstate().  By default, the package runs with 128 bytes of state
+// information and generates far better random numbers than a linear
+// congruential generator.  If the amount of state information is less than
+// 32 bytes, a simple linear congruential R.N.G. is used.
+//
+// Internally, the state information is treated as an array of longs; the
+// zeroeth element of the array is the type of R.N.G. being used (small
+// integer); the remainder of the array is the state information for the
+// R.N.G.  Thus, 32 bytes of state information will give 7 longs worth of
+// state information, which will allow a degree seven polynomial.  (Note:
+// the zeroeth word of state information also has some other information
+// stored in it -- see setstate() for details).
+//
+// The random number generation technique is a linear feedback shift register
+// approach, employing trinomials (since there are fewer terms to sum up that
+// way).  In this approach, the least significant bit of all the numbers in
+// the state table will act as a linear feedback shift register, and will
+// have period 2^deg - 1 (where deg is the degree of the polynomial being
+// used, assuming that the polynomial is irreducible and primitive).  The
+// higher order bits will have longer periods, since their values are also
+// influenced by pseudo-random carries out of the lower bits.  The total
+// period of the generator is approximately deg*(2**deg - 1); thus doubling
+// the amount of state information has a vast influence on the period of the
+// generator.  Note: the deg*(2**deg - 1) is an approximation only good for
+// large deg, when the period of the shift is the dominant factor.
+// With deg equal to seven, the period is actually much longer than the
+// 7*(2**7 - 1) predicted by this formula.
+//
+// Modified 28 December 1994 by Jacob S. Rosenberg.
+//
+
+//
+// For each of the currently supported random number generators, we have a
+// break value on the amount of state information (you need at least this
+// many bytes of state info to support this random number generator), a degree
+// for the polynomial (actually a trinomial) that the R.N.G. is based on, and
+// the separation between the two lower order coefficients of the trinomial.
+
+void RandomGenerator::initialize() {
+
+#define NSHUFF 100      // To drop part of seed -> 1st value correlation
+
+//static long degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
+//static long seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
+
+    degrees[0] = DEG_0;
+    degrees[1] = DEG_1;
+    degrees[2] = DEG_2;
+    degrees[3] = DEG_3;
+    degrees[4] = DEG_4;
+
+    seps [0] = SEP_0;
+    seps [1] = SEP_1;
+    seps [2] = SEP_2;
+    seps [3] = SEP_3;
+    seps [4] = SEP_4;
+
+//
+// Initially, everything is set up as if from:
+//
+//  initstate(1, randtbl, 128);
+//
+// Note that this initialization takes advantage of the fact that srandom()
+// advances the front and rear pointers 10*rand_deg times, and hence the
+// rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+// element of the state information, which contains info about the current
+// position of the rear pointer is just
+//
+//  MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
+
+    randtbl[ 0] =  TYPE_3;
+    randtbl[ 1] =  0x991539b1;
+    randtbl[ 2] =  0x16a5bce3;
+    randtbl[ 3] =  0x6774a4cd;
+    randtbl[ 4] =  0x3e01511e;
+    randtbl[ 5] =  0x4e508aaa;
+    randtbl[ 6] =  0x61048c05;
+    randtbl[ 7] =  0xf5500617;
+    randtbl[ 8] =  0x846b7115;
+    randtbl[ 9] =  0x6a19892c;
+    randtbl[10] =  0x896a97af;
+    randtbl[11] =  0xdb48f936;
+    randtbl[12] =  0x14898454;
+    randtbl[13] =  0x37ffd106;
+    randtbl[14] =  0xb58bff9c;
+    randtbl[15] =  0x59e17104;
+    randtbl[16] =  0xcf918a49;
+    randtbl[17] =  0x09378c83;
+    randtbl[18] =  0x52c7a471;
+    randtbl[19] =  0x8d293ea9;
+    randtbl[20] =  0x1f4fc301;
+    randtbl[21] =  0xc3db71be;
+    randtbl[22] =  0x39b44e1c;
+    randtbl[23] =  0xf8a44ef9;
+    randtbl[24] =  0x4c8b80b1;
+    randtbl[25] =  0x19edc328;
+    randtbl[26] =  0x87bf4bdd;
+    randtbl[27] =  0xc9b240e5;
+    randtbl[28] =  0xe9ee4b1b;
+    randtbl[29] =  0x4382aee7;
+    randtbl[30] =  0x535b6b41;
+    randtbl[31] =  0xf3bec5da;
+
+// static long randtbl[DEG_3 + 1] =
+// {
+//   TYPE_3;
+//   0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05,
+//   0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454,
+//   0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471,
+//   0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1,
+//   0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41,
+//   0xf3bec5da
+// };
+
+
+//
+// fptr and rptr are two pointers into the state info, a front and a rear
+// pointer.  These two pointers are always rand_sep places aparts, as they
+// cycle cyclically through the state information.  (Yes, this does mean we
+// could get away with just one pointer, but the code for random() is more
+// efficient this way).  The pointers are left positioned as they would be
+// from the call
+//
+//  initstate(1, randtbl, 128);
+//
+// (The position of the rear pointer, rptr, is really 0 (as explained above
+// in the initialization of randtbl) because the state table pointer is set
+// to point to randtbl[1] (as explained below).
+//
+
+    fptr = &randtbl[SEP_3 + 1];
+    rptr = &randtbl[1];
+
+//
+// The following things are the pointer to the state information table, the
+// type of the current generator, the degree of the current polynomial being
+// used, and the separation between the two pointers.  Note that for efficiency
+// of random(), we remember the first location of the state information, not
+// the zeroeth.  Hence it is valid to access state[-1], which is used to
+// store the type of the R.N.G.  Also, we remember the last location, since
+// this is more efficient than indexing every time to find the address of
+// the last element to see if the front and rear pointers have wrapped.
+//
+
+    state = &randtbl[1];
+    rand_type = TYPE_3;
+    rand_deg = DEG_3;
+    rand_sep = SEP_3;
+    end_ptr = &randtbl[DEG_3 + 1];
+
+}
+
+//
+// Compute x = (7^5 * x) mod (2^31 - 1)
+// wihout overflowing 31 bits:
+//      (2^31 - 1) = 127773 * (7^5) + 2836
+// From "Random number generators: good ones are hard to find",
+// Park and Miller, Communications of the ACM, vol. 31, no. 10,
+// October 1988, p. 1195.
+//
+
+__inline static long good_rand(long x)
+{
+  long hi, lo;
+
+  // Can't be initialized with 0, so use another value.
+  if (x == 0) x = 123459876;
+  hi = x / 127773;
+  lo = x % 127773;
+  x = 16807 * lo - 2836 * hi;
+  if (x < 0) x += 0x7fffffff;
+  return x;
+}
+
+//
+// srandom
+//
+// Initialize the random number generator based on the given seed.  If the
+// type is the trivial no-state-information type, just remember the seed.
+// Otherwise, initializes state[] based on the given "seed" via a linear
+// congruential generator.  Then, the pointers are set to known locations
+// that are exactly rand_sep places apart.  Lastly, it cycles the state
+// information a given number of times to get rid of any initial dependencies
+// introduced by the L.C.R.N.G.  Note that the initialization of randtbl[]
+// for default usage relies on values produced by this routine.
+
+void RandomGenerator::srandom(unsigned long x)
+{
+  long i, lim;
+
+  state[0] = x;
+  if (rand_type == TYPE_0)
+    lim = NSHUFF;
+  else
+  {
+    for (i = 1; i < rand_deg; i++) state[i] = good_rand(state[i - 1]);
+    fptr = &state[rand_sep];
+    rptr = &state[0];
+    lim = 10 * rand_deg;
+  }
+
+  initialized = 1;
+  for (i = 0; i < lim; i++) random();
+}
+
+#ifdef NOT_FOR_SUPERTUX     // use in supertux doesn't require these methods,
+                            // which are not portable to as many platforms as
+                            // SDL.  The cost is that the variability of the
+                            // initial seed is reduced to only 32 bits of
+                            // randomness, seemingly enough. PAK 060420
+//
+// srandomdev
+//
+// Many programs choose the seed value in a totally predictable manner.
+// This often causes problems.  We seed the generator using the much more
+// secure random() interface.  Note that this particular seeding
+// procedure can generate states which are impossible to reproduce by
+// calling srandom() with any value, since the succeeding terms in the
+// state buffer are no longer derived from the LC algorithm applied to
+// a fixed seed.
+
+void RandomGenerator::srandomdev()
+{
+  int fd, done;
+  size_t len;
+
+  if (rand_type == TYPE_0)
+    len = sizeof state[0];
+  else
+    len = rand_deg * sizeof state[0];
+
+  done = 0;
+  fd = open("/dev/urandom", O_RDONLY);
+  if (fd >= 0)
+   {
+     if (read(fd, state, len) == len) done = 1;
+     close(fd);
+   }
+
+  if (!done)
+  {
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    srandom(tv.tv_sec ^ tv.tv_usec);
+    return;
+  }
+
+  if (rand_type != TYPE_0)
+  {
+    fptr = &state[rand_sep];
+    rptr = &state[0];
+  }
+  initialized = 1;
+}
+
+//
+// initstate
+//
+// Initialize the state information in the given array of n bytes for future
+// random number generation.  Based on the number of bytes we are given, and
+// the break values for the different R.N.G.'s, we choose the best (largest)
+// one we can and set things up for it.  srandom() is then called to
+// initialize the state information.
+//
+// Note that on return from srandom(), we set state[-1] to be the type
+// multiplexed with the current value of the rear pointer; this is so
+// successive calls to initstate() won't lose this information and will be
+// able to restart with setstate().
+//
+// Note: the first thing we do is save the current state, if any, just like
+// setstate() so that it doesn't matter when initstate is called.
+//
+// Returns a pointer to the old state.
+//
+
+char * RandomGenerator::initstate(unsigned long seed, char *arg_state, long n)
+{
+  char *ostate = (char *) (&state[-1]);
+  long *long_arg_state = (long *) arg_state;
+
+  if (rand_type == TYPE_0)
+    state[-1] = rand_type;
+  else
+    state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+  if (n < BREAK_0) return NULL;
+
+  if (n < BREAK_1)
+  {
+    rand_type = TYPE_0;
+    rand_deg = DEG_0;
+    rand_sep = SEP_0;
+  }
+  else if (n < BREAK_2)
+  {
+    rand_type = TYPE_1;
+    rand_deg = DEG_1;
+    rand_sep = SEP_1;
+  }
+  else if (n < BREAK_3)
+  {
+    rand_type = TYPE_2;
+    rand_deg = DEG_2;
+    rand_sep = SEP_2;
+  }
+  else if (n < BREAK_4)
+  {
+    rand_type = TYPE_3;
+    rand_deg = DEG_3;
+    rand_sep = SEP_3;
+  }
+  else
+  {
+    rand_type = TYPE_4;
+    rand_deg = DEG_4;
+    rand_sep = SEP_4;
+  }
+
+  state = (long *) (long_arg_state + 1); // First location
+  end_ptr = &state[rand_deg]; // Must set end_ptr before srandom
+  srandom(seed);
+
+  if (rand_type == TYPE_0)
+    long_arg_state[0] = rand_type;
+  else
+    long_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type;
+
+  initialized = 1;
+  return ostate;
+}
+
+//
+// setstate
+//
+// Restore the state from the given state array.
+//
+// Note: it is important that we also remember the locations of the pointers
+// in the current state information, and restore the locations of the pointers
+// from the old state information.  This is done by multiplexing the pointer
+// location into the zeroeth word of the state information.
+//
+// Note that due to the order in which things are done, it is OK to call
+// setstate() with the same state as the current state.
+//
+// Returns a pointer to the old state information.
+//
+
+char * RandomGenerator::setstate(char *arg_state)
+{
+  long *new_state = (long *) arg_state;
+  long type = new_state[0] % MAX_TYPES;
+  long rear = new_state[0] / MAX_TYPES;
+  char *ostate = (char *) (&state[-1]);
+
+  if (rand_type == TYPE_0)
+    state[-1] = rand_type;
+  else
+    state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+  switch(type)
+  {
+    case TYPE_0:
+    case TYPE_1:
+    case TYPE_2:
+    case TYPE_3:
+    case TYPE_4:
+      rand_type = type;
+      rand_deg = degrees[type];
+      rand_sep = seps[type];
+      break;
+  }
+
+  state = (long *) (new_state + 1);
+  if (rand_type != TYPE_0)
+  {
+    rptr = &state[rear];
+    fptr = &state[(rear + rand_sep) % rand_deg];
+  }
+  end_ptr = &state[rand_deg];   // Set end_ptr too
+
+  initialized = 1;
+  return ostate;
+}
+#endif //NOT_FOR_SUPERTUX
+//
+// random:
+//
+// If we are using the trivial TYPE_0 R.N.G., just do the old linear
+// congruential bit.  Otherwise, we do our fancy trinomial stuff, which is
+// the same in all the other cases due to all the global variables that have
+// been set up.  The basic operation is to add the number at the rear pointer
+// into the one at the front pointer.  Then both pointers are advanced to
+// the next location cyclically in the table.  The value returned is the sum
+// generated, reduced to 31 bits by throwing away the "least random" low bit.
+//
+// Note: the code takes advantage of the fact that both the front and
+// rear pointers can't wrap on the same call by not testing the rear
+// pointer if the front one has wrapped.
+//
+// Returns a 31-bit random number.
+//
+
+long RandomGenerator::random()
+{
+  long i;
+  long *f, *r;
+  if (!initialized) {
+      throw std::runtime_error("uninitialized RandomGenerator object");
+  }
+
+  if (rand_type == TYPE_0)
+  {
+    i = state[0];
+    state[0] = i = (good_rand(i)) & 0x7fffffff;
+  }
+  else
+  {
+    f = fptr; r = rptr;
+    *f += *r;
+    i = (*f >> 1) & 0x7fffffff; // Chucking least random bit
+    if (++f >= end_ptr)
+    {
+      f = state;
+      ++r;
+    }
+    else if (++r >= end_ptr)
+      r = state;
+
+    fptr = f; rptr = r;
+  }
+
+  return i;
+}
diff --git a/src/random_generator.hpp b/src/random_generator.hpp
new file mode 100644 (file)
index 0000000..5e5b3c6
--- /dev/null
@@ -0,0 +1,127 @@
+// $Id$
+//
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. 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.
+// 3. Neither the name of the project 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.
+
+#ifndef __RANDOM_GENERATOR__
+#define __RANDOM_GENERATOR__
+
+class RandomGenerator
+{
+private:
+// Array versions of the above information to make code run faster --
+// relies on fact that TYPE_i == i.
+    static const int TYPE_0 = 0;   // Linear congruential
+    static const int BREAK_0 = 8;
+    static const int DEG_0 = 0;
+    static const int SEP_0 = 0;
+
+    static const int TYPE_1 = 1;   // x**7 + x**3 + 1
+    static const int BREAK_1 = 32;
+    static const int DEG_1 = 7;
+    static const int SEP_1 = 3;
+
+    static const int TYPE_2 = 2;   // x**15 + x + 1
+    static const int BREAK_2 = 64;
+    static const int DEG_2 = 15;
+    static const int SEP_2 = 1;
+
+    static const int TYPE_3 = 3;   // x**31 + x**3 + 1
+    static const int BREAK_3 = 128;
+    static const int DEG_3 = 31;
+    static const int SEP_3 = 3;
+
+    static const int TYPE_4 = 4;   // x**63 + x + 1
+    static const int BREAK_4 = 256;
+    static const int DEG_4 = 63;
+    static const int SEP_4 = 1;
+
+    static const int MAX_TYPES = 5;     // Max number of types above
+
+    bool initialized;
+    long degrees[MAX_TYPES];
+    long seps [MAX_TYPES];
+    long randtbl[DEG_3 + 1];
+
+    long *fptr;
+    long *rptr;
+
+    long *state;
+    long rand_type;
+    long rand_deg;
+    long rand_sep;
+    long *end_ptr;
+    int debug;
+    static const int rand_max = 0x7fffffff;         // biggest signed Uint32
+
+public:
+    RandomGenerator();
+    ~RandomGenerator();
+
+// Documentation of user-visible calls:
+
+     // Initialize the RNG with a 31-bit seed
+    // if x is zero or absent, calls to time() will get a time-randomized seed
+    // the value returned is the value of the seed used.
+    int srand(int x=0);
+
+     // generate random 31-bit numbers
+    // calls to the following return a value evenly distributed between u (or
+    // 0 if not specified) and v (or rand_max if not specified).  Return
+    // values may include u, but never v.
+    int rand();
+    int rand(int v);
+    int rand(int u, int v);
+    double randf(double v);
+    double randf(double u, double v);
+
+    // For Squirrel wrapper, since miniswig (and even squirrel?) doesn't
+    // support function overloading or doubles
+    int rand1i(int v) { return rand(v); }
+    int rand2i(int u, int v) { return rand(u, v); }
+    float rand1f(float v)
+      { return static_cast<float>(randf(static_cast<double>(v))); }
+    float rand2f(float u, float v)
+      { return static_cast<float>(randf(static_cast<double>(u),
+                                      static_cast<double>(v))); }
+
+//private:
+    void initialize();
+    void srandom(unsigned long x);
+//  void srandomdev();
+//  char *initstate(unsigned long seed, char *arg_state, long n);
+//  char *setstate(char *arg_state);
+    long random();
+};
+
+extern RandomGenerator systemRandom;
+
+#endif //__RANDOM_GENERATOR__
diff --git a/src/ref.hpp b/src/ref.hpp
new file mode 100644 (file)
index 0000000..4e15c0f
--- /dev/null
@@ -0,0 +1,87 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __REF_HPP__
+#define __REF_HPP__
+
+/** This class behaves like a pointer to a refcounted object, but increments the
+ * reference count when new objects are assigned and decrements the refcounter
+ * when it's lifetime has experied. (similar to std::auto_ptr)
+ */
+template<typename T>
+class Ref
+{
+public:
+  Ref(T* object = 0)
+    : object(object)
+  {
+    if(object)
+      object->ref();
+  }
+  Ref(const Ref<T>& other)
+    : object(other.object)
+  {
+    if(object)
+      object->ref();
+  }
+  ~Ref()
+  {
+    if(object)
+      object->unref();
+  }
+
+  void operator= (const Ref<T>& other)
+  {
+    *this = other.get();
+  }
+
+  void operator= (T* object)
+  {
+    if(object)
+      object->ref();
+    if(this->object)
+      this->object->unref();
+    this->object = object;
+  }
+
+  T* operator ->() const
+  {
+    return object;
+  }
+
+  T& operator* () const
+  {
+    return *object;
+  }
+
+  operator const T* () const
+  {
+    return object;
+  }
+
+  T* get() const
+  {
+    return object;
+  }
+
+private:
+  T* object;
+};
+
+#endif
diff --git a/src/refcounter.hpp b/src/refcounter.hpp
new file mode 100644 (file)
index 0000000..e810efe
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  Windstille - A Jump'n Shoot Game
+//  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __REFCOUNTER_HPP__
+#define __REFCOUNTER_HPP__
+
+#include <assert.h>
+
+/**
+ * A base class that provides reference counting facilities
+ */
+class RefCounter
+{
+public:
+  RefCounter()
+    : refcount(0)
+  { }
+
+  /** increases reference count */
+  void ref()
+  {
+    refcount++;
+  }
+  /** decreases reference count. Destroys the object if the reference count
+   * reaches 0
+   */
+  void unref()
+  {
+    refcount--;
+    if(refcount <= 0) {
+      delete this;
+    }
+  }
+
+protected:
+  virtual ~RefCounter()
+  {
+    assert(refcount == 0);
+  }
+
+private:
+  int refcount;
+};
+
+#endif
diff --git a/src/resources.cpp b/src/resources.cpp
new file mode 100644 (file)
index 0000000..f0e6037
--- /dev/null
@@ -0,0 +1,152 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "sprite/sprite_manager.hpp"
+#include "gui/menu.hpp"
+#include "gui/button.hpp"
+#include "resources.hpp"
+#include "file_system.hpp"
+#include "tile_manager.hpp"
+#include "object/gameobjs.hpp"
+#include "object/player.hpp"
+
+MouseCursor* mouse_cursor = NULL;
+
+Font* gold_text = NULL;
+Font* gold_fixed_text = NULL;
+Font* blue_text = NULL;
+Font* gray_text = NULL;
+Font* white_text = NULL;
+Font* white_small_text = NULL;
+Font* white_big_text = NULL;
+
+/* Load graphics/sounds shared between all levels: */
+void load_shared()
+{
+  /* Load the mouse-cursor */
+  mouse_cursor = new MouseCursor("images/engine/menu/mousecursor.png");
+  MouseCursor::set_current(mouse_cursor);
+
+  /* Load global images: */
+  gold_text  = new Font(Font::VARIABLE,
+                        "images/engine/fonts/gold.png",
+                        "images/engine/fonts/shadow.png", 16, 18);
+  gold_fixed_text  = new Font(Font::FIXED,
+                        "images/engine/fonts/gold.png",
+                        "images/engine/fonts/shadow.png", 16, 18);
+  blue_text  = new Font(Font::VARIABLE,
+                        "images/engine/fonts/blue.png",
+                        "images/engine/fonts/shadow.png", 16, 18, 3);
+  white_text = new Font(Font::VARIABLE,
+                        "images/engine/fonts/white.png",
+                        "images/engine/fonts/shadow.png", 16, 18);
+  gray_text  = new Font(Font::VARIABLE,
+                        "images/engine/fonts/gray.png",
+                       "images/engine/fonts/shadow.png", 16, 18);
+  white_small_text = new Font(Font::VARIABLE,
+                              "images/engine/fonts/white-small.png",
+                              "images/engine/fonts/shadow-small.png", 8, 9, 1);
+  white_big_text = new Font(Font::VARIABLE,
+                            "images/engine/fonts/white-big.png",
+                            "images/engine/fonts/shadow-big.png", 20, 22, 3);
+
+  Menu::default_font  = white_text;
+  Menu::active_font   = blue_text;
+  Menu::deactive_font = gray_text;
+  Menu::label_font    = white_big_text;
+  Menu::field_font    = gold_text;
+
+  Button::info_font = white_small_text;
+
+  sprite_manager = new SpriteManager();
+  tile_manager   = new TileManager("images/tiles.strf");
+
+  /* Tuxes: */
+  char img_name[1024];
+  for (int i = 0; i < GROWING_FRAMES; i++)
+    {
+      snprintf(img_name, sizeof(img_name), "images/creatures/tux_grow/left-%i.png", i+1);
+      growingtux_left[i] = new Surface(img_name);
+
+      snprintf(img_name, sizeof(img_name), "images/creatures/tux_grow/right-%i.png", i+1);
+      growingtux_right[i] = new Surface(img_name);
+    }
+
+  small_tux = new TuxBodyParts();
+  small_tux->head = 0;
+  small_tux->body = sprite_manager->create("images/creatures/tux_small/small-tux-body.sprite");
+  small_tux->arms = sprite_manager->create("images/creatures/tux_small/small-tux-arms.sprite");
+  small_tux->feet = 0;
+
+  big_tux = new TuxBodyParts();
+  big_tux->head = sprite_manager->create("images/creatures/tux_big/big-tux-head.sprite");
+  big_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+  big_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+  big_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+  fire_tux = new TuxBodyParts();
+  fire_tux->head = sprite_manager->create("images/creatures/tux_big/big-fire-tux-head.sprite");
+  fire_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+  fire_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+  fire_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+  ice_tux = new TuxBodyParts();
+  ice_tux->head = sprite_manager->create("images/creatures/tux_big/big-ice-tux-head.sprite");
+  ice_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+  ice_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+  ice_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+  player_status = new PlayerStatus();
+}
+
+/* Free shared data: */
+void unload_shared()
+{
+  /* Free global images: */
+  delete gold_text;
+  delete gold_fixed_text;
+  delete white_text;
+  delete blue_text;
+  delete gray_text;
+  delete white_small_text;
+  delete white_big_text;
+
+  delete small_tux;
+  delete big_tux;
+  delete fire_tux;
+  delete ice_tux;
+
+  for (int i = 0; i < GROWING_FRAMES; i++) {
+    delete growingtux_left[i];
+    delete growingtux_right[i];
+  }
+
+  delete sprite_manager;
+  sprite_manager = NULL;
+  delete tile_manager;
+  tile_manager = NULL;
+
+  /* Free mouse-cursor */
+  delete mouse_cursor;
+
+  delete player_status;
+  player_status = NULL;
+}
diff --git a/src/resources.hpp b/src/resources.hpp
new file mode 100644 (file)
index 0000000..27d1e98
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_RESOURCES_H
+#define SUPERTUX_RESOURCES_H
+
+class Font;
+class MouseCursor;
+
+extern MouseCursor* mouse_cursor;
+
+extern Font* gold_text;
+extern Font* gold_fixed_text;
+extern Font* white_text;
+extern Font* blue_text;
+extern Font* gray_text;
+extern Font* white_small_text;
+extern Font* white_big_text;
+
+void load_shared();
+void unload_shared();
+
+#endif
diff --git a/src/screen.hpp b/src/screen.hpp
new file mode 100644 (file)
index 0000000..a2b753c
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SCREEN_HPP__
+#define __SCREEN_HPP__
+
+class DrawingContext;
+
+class Screen
+{
+public:
+  virtual ~Screen()
+  {}
+
+  /**
+   * gets called before this screen gets activated (which is at least once
+   * before the first draw or update call
+   */
+  virtual void setup()
+  {}
+  /** gets called when the current screen is temporarily suspended */
+  virtual void leave()
+  {}
+
+  /**
+   * gets called once per frame. The screen should draw itself in this function.
+   * State changes should not be done in this function, but rather in update
+   */
+  virtual void draw(DrawingContext& context) = 0;
+
+  /**
+   * gets called for once (per logical) frame. Screens should do their state
+   * updates and logic here
+   */
+  virtual void update(float elapsed_time) = 0;
+};
+
+#endif
diff --git a/src/screen_fade.hpp b/src/screen_fade.hpp
new file mode 100644 (file)
index 0000000..952d034
--- /dev/null
@@ -0,0 +1,38 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SCREENFADE_HPP__
+#define __SCREENFADE_HPP__
+
+#include "screen.hpp"
+
+/**
+ * A ScreenFade screen is displayed simultaneously with another screen. This
+ * is intended to be used for transitional effects like fade-out or shrink-fade
+ */
+class ScreenFade : public Screen
+{
+public:
+  virtual ~ScreenFade()
+  {}
+
+  /// returns true if the effect is completed
+  virtual bool done() = 0;
+};
+
+#endif
diff --git a/src/script_interface.hpp b/src/script_interface.hpp
new file mode 100644 (file)
index 0000000..3f47bcf
--- /dev/null
@@ -0,0 +1,39 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __SCRIPT_INTERFACE_HPP__
+#define __SCRIPT_INTERFACE_HPP__
+
+#include <squirrel.h>
+
+/**
+ * Objects that want to expose themself to the scripting environment
+ * should implement this interface
+ */
+class ScriptInterface
+{
+public:
+  virtual ~ScriptInterface()
+  {}
+
+  virtual void expose(HSQUIRRELVM vm, SQInteger table_idx) = 0;
+  virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx) = 0;
+};
+
+#endif
diff --git a/src/scripting/Jamfile b/src/scripting/Jamfile
new file mode 100644 (file)
index 0000000..a6df87e
--- /dev/null
@@ -0,0 +1,54 @@
+SubDir TOP src scripting ;
+
+if $(MINISWIG)
+{
+    ##  MiniSwigRule outputcppfile : inputfile : modulename : flags
+    rule MiniSwigRule
+    {
+        local sources = [ SearchSource $(>) ] ;
+        local cppfile = [ LocateTarget $(<) : $(SUBDIR) ] ;
+        local headerfile = [ LocateTarget $(<:S=.hpp) : $(SUBDIR) ] ;  
+        SEARCH on $(headerfile) = $(SOURCH_SOURCE) ;
+
+        MiniSwig $(cppfile) : $(sources) ;
+
+        CPPFLAGS on $(cppfile) = $(CPPFLAGS) -DSCRIPTING_API ;
+        headerfile on $(cppfile) = $(headerfile) ;
+        modulename on $(cppfile) = $(3) ;
+        FLAGS on $(cppfile) = $(4) ;
+
+        local h = $(headerfile:G=) ;
+        h = $(h:D=) ;
+        Includes $(h) : $(headerfile) ;
+        Includes $(headerfile) : $(cppfile) ;
+
+        local object = [ CompileObject $(cppfile) ] ;
+
+        return $(object) ;
+    }
+
+    rule MiniSwig
+    {
+        Depends $(<) : $(>) $(MINISWIG) ;
+    }
+
+    actions MiniSwig bind headerfile
+    {
+        $(CPP) -x c -CC $(CPPFLAGS) $(>) -o $(LOCATE_OBJECTS)/miniswig.tmp
+        ./miniswig --output-cpp $(<) --input $(LOCATE_OBJECTS)/miniswig.tmp --output-hpp $(headerfile) --module $(modulename) $(FLAGS)
+#       rm -f $(LOCATE_OBJECTS)/miniswig.tmp
+    }
+}
+
+wrapper_sources = [ Filter [ Wildcard *.cpp *.hpp ] : wrapper.cpp wrapper.hpp ] ;
+if ! $(MINISWIG)
+{
+    wrapper_sources += [ SearchSource wrapper.cpp ] ;
+}
+wrapper_objects = [ CompileObjects $(wrapper_sources) ] ;
+if $(MINISWIG)
+{
+    wrapper_objects += 
+        [ MiniSwigRule wrapper.cpp : wrapper.interface.hpp : supertux : --select-namespace Scripting ] ;
+}
+
diff --git a/src/scripting/ambient_sound.hpp b/src/scripting/ambient_sound.hpp
new file mode 100644 (file)
index 0000000..7fefdb3
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __SCRIPTING_AMBIENT_SOUND_H__
+#define __SCRIPTING_AMBIENT_SOUND_H__
+
+namespace Scripting
+{
+
+class AmbientSound
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~AmbientSound()
+  {}
+#endif
+
+  virtual void set_pos(float x, float y) = 0;
+  virtual float get_pos_x() const = 0;
+  virtual float get_pos_y() const = 0;
+};
+
+}
+
+#endif
diff --git a/src/scripting/anchor_points.hpp b/src/scripting/anchor_points.hpp
new file mode 100644 (file)
index 0000000..a0dff38
--- /dev/null
@@ -0,0 +1,38 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __ANCHOR_POINTS_HPP__
+#define __ANCHOR_POINTS_HPP__
+
+namespace Scripting {
+
+// TODO get these from the definitions in anchor.h (needs miniswig update)
+static const int ANCHOR_TOP         = 0x0010;
+static const int ANCHOR_BOTTOM      = 0x0020;
+static const int ANCHOR_LEFT        = 0x0001;
+static const int ANCHOR_RIGHT       = 0x0002;
+static const int ANCHOR_MIDDLE      = 0x0000;
+static const int ANCHOR_TOP_LEFT    = 0x0011;
+static const int ANCHOR_TOP_RIGHT   = 0x0012;
+static const int ANCHOR_BOTTOM_LEFT = 0x0021;
+static const int ANCHOR_BOTTOM_RIGHT = 0x0022;
+
+}
+
+#endif
diff --git a/src/scripting/camera.cpp b/src/scripting/camera.cpp
new file mode 100644 (file)
index 0000000..ea90989
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/camera.hpp"
+#include "scripting/camera.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+  Camera::Camera(::Camera* camera)
+    : camera(camera)
+  { }
+
+  Camera::~Camera()
+  { }
+
+  void
+  Camera::reload_config()
+  {
+    camera->reload_config();
+  }
+
+  void
+  Camera::shake(float speed, float x, float y)
+  {
+    camera->shake(speed, x, y);
+  }
+
+  void
+  Camera::set_pos(float , float )
+  {
+  }
+
+  void
+  Camera::set_mode(const std::string& mode)
+  {
+    if(mode == "normal") {
+      camera->mode = ::Camera::NORMAL;
+    } else if(mode == "manual") {
+      camera->mode = ::Camera::MANUAL;
+    } else {
+      log_fatal << "Camera mode '" << mode << "' unknown.";
+    }
+  }
+
+  void
+  Camera::scroll_to(float x, float y, float scrolltime)
+  {
+    camera->scroll_to(Vector(x, y), scrolltime);
+  }
+}
diff --git a/src/scripting/camera.hpp b/src/scripting/camera.hpp
new file mode 100644 (file)
index 0000000..558c207
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __CAMERA_H__
+#define __CAMERA_H__
+
+#ifndef SCRIPTING_API
+class Camera;
+typedef Camera _Camera;
+#endif
+
+namespace Scripting
+{
+
+class Camera
+{
+public:
+#ifndef SCRIPTING_API
+  Camera(_Camera* camera);
+  ~Camera();
+#endif
+
+  void reload_config();
+
+  /** Shake the camera */
+  void shake(float speed, float x, float y);
+  /** Set camera to a specific coordinate */
+  void set_pos(float x, float y);
+  /** Set camera to a specific mode, can be "normal", "manual" */
+  void set_mode(const std::string& mode);
+  /** Scroll camera to position x,y in scrolltime seconds */
+  void scroll_to(float x, float y, float scrolltime);
+
+#ifndef SCRIPTING_API
+  _Camera* camera;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/candle.cpp b/src/scripting/candle.cpp
new file mode 100644 (file)
index 0000000..0223521
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/candle.hpp"
+#include "scripting/candle.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  Candle::Candle(::Candle* candle)
+    : candle(candle)
+  { }
+
+  Candle::~Candle()
+  { }
+
+  bool Candle::get_burning()
+  {
+    return candle->get_burning();
+  }
+
+  void Candle::set_burning(bool burning)
+  {
+    candle->set_burning(burning);
+  }
+
+}
diff --git a/src/scripting/candle.hpp b/src/scripting/candle.hpp
new file mode 100644 (file)
index 0000000..be64bab
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_CANDLE_H__
+#define __SCRIPTING_CANDLE_H__
+
+#ifndef SCRIPTING_API
+class Candle;
+typedef Candle _Candle;
+#endif
+
+namespace Scripting
+{
+
+class Candle
+{
+public:
+#ifndef SCRIPTING_API
+  Candle(_Candle* candle);
+  ~Candle();
+#endif
+
+  bool get_burning(); /**< returns true if candle is lighted */
+  void set_burning(bool burning); /**< true: light candle, false: extinguish candle */
+
+#ifndef SCRIPTING_API
+  _Candle* candle;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/display_effect.hpp b/src/scripting/display_effect.hpp
new file mode 100644 (file)
index 0000000..3365b09
--- /dev/null
@@ -0,0 +1,54 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_DISPLAY_EFFECT_H__
+#define __SCRIPTING_DISPLAY_EFFECT_H__
+
+namespace Scripting
+{
+
+class DisplayEffect
+{
+public:
+#ifndef SCRIPTING_API
+    virtual ~DisplayEffect()
+    {}
+#endif
+
+    /// fade display to black
+    virtual void fade_out(float fadetime) = 0;
+    /// fade display from black to normal
+    virtual void fade_in(float fadetime) = 0;
+    /// set display black (or back to normal)
+    virtual void set_black(bool enabled) = 0;
+    /// check if display is set to black
+    virtual bool is_black() = 0;
+    /// set black borders for cutscenes
+    virtual void sixteen_to_nine(float fadetime) = 0;
+    /// deactivate borders
+    virtual void four_to_three(float fadetime) = 0;
+
+    // fade display until just a small visible circle is left
+    // (like what happens in some cartoons at the end)
+    // void shrink_fade(Vector goal, float radius, float fadetime);
+};
+
+}
+
+#endif
diff --git a/src/scripting/floating_image.cpp b/src/scripting/floating_image.cpp
new file mode 100644 (file)
index 0000000..1cb0c02
--- /dev/null
@@ -0,0 +1,129 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <assert.h>
+#include <stdexcept>
+#include "floating_image.hpp"
+#include "sector.hpp"
+#include "object/floating_image.hpp"
+#include "worldmap/worldmap.hpp"
+
+namespace Scripting
+{
+
+FloatingImage::FloatingImage(const std::string& spritefile)
+{
+  using namespace WorldMapNS;
+
+  floating_image = new _FloatingImage(spritefile);
+  if(Sector::current() != NULL) {
+    Sector::current()->add_object(floating_image.get());
+  } else if(WorldMap::current() != NULL) {
+    WorldMap::current()->add_object(floating_image.get());
+  } else {
+    throw new std::runtime_error("Neither sector nor worldmap active");
+  }
+}
+
+FloatingImage::~FloatingImage()
+{
+  floating_image->remove_me();
+}
+
+void
+FloatingImage::set_layer(int layer)
+{
+  floating_image->set_layer(layer);
+}
+
+int
+FloatingImage::get_layer()
+{
+  return floating_image->get_layer();
+}
+
+void
+FloatingImage::set_pos(float x, float y)
+{
+  floating_image->set_pos(Vector(x, y));
+}
+
+float
+FloatingImage::get_pos_x()
+{
+  return floating_image->get_pos().x;
+}
+
+float
+FloatingImage::get_pos_y()
+{
+  return floating_image->get_pos().y;
+}
+
+void
+FloatingImage::set_anchor_point(int anchor)
+{
+  floating_image->set_anchor_point((AnchorPoint) anchor);
+}
+
+int
+FloatingImage::get_anchor_point()
+{
+  return (int) floating_image->get_anchor_point();
+}
+
+bool
+FloatingImage::get_visible()
+{
+  return floating_image->get_visible();
+}
+
+void
+FloatingImage::set_visible(bool visible)
+{
+  floating_image->set_visible(visible);
+}
+
+void
+FloatingImage::set_action(const std::string& action)
+{
+  floating_image->set_action(action);
+}
+
+std::string
+FloatingImage::get_action()
+{
+  return floating_image->get_action();
+}
+
+void
+FloatingImage::fade_in(float fadetime)
+{
+  floating_image->fade_in(fadetime);
+}
+
+void
+FloatingImage::fade_out(float fadetime)
+{
+  floating_image->fade_out(fadetime);
+}
+
+
+}
diff --git a/src/scripting/floating_image.hpp b/src/scripting/floating_image.hpp
new file mode 100644 (file)
index 0000000..7fd9206
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FLOATING_IMAGE_HPP__
+#define __FLOATING_IMAGE_HPP__
+
+#ifndef SCRIPTING_API
+#define __suspend
+#include <string>
+#include "ref.hpp"
+
+class FloatingImage;
+typedef FloatingImage _FloatingImage;
+#endif
+
+namespace Scripting
+{
+
+class FloatingImage
+{
+public:
+  FloatingImage(const std::string& spritefile);
+  ~FloatingImage();
+
+  void set_layer(int layer);
+  int get_layer();
+  void set_pos(float x, float y);
+  float get_pos_x();
+  float get_pos_y();
+  void set_anchor_point(int anchor);
+  int get_anchor_point();
+  void set_visible(bool visible);
+  bool get_visible();
+  void set_action(const std::string& action);
+  std::string get_action();
+  void fade_in(float fadetime);
+  void fade_out(float fadetime);
+
+#ifndef SCRIPTING_API
+private:
+  Ref<_FloatingImage> floating_image;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/functions.cpp b/src/scripting/functions.cpp
new file mode 100644 (file)
index 0000000..36e4705
--- /dev/null
@@ -0,0 +1,288 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <memory>
+#include <stdio.h>
+#include <string>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include "textscroller.hpp"
+#include "functions.hpp"
+#include "game_session.hpp"
+#include "tinygettext/tinygettext.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "worldmap/worldmap.hpp"
+#include "world.hpp"
+#include "sector.hpp"
+#include "gameconfig.hpp"
+#include "object/player.hpp"
+#include "object/tilemap.hpp"
+#include "main.hpp"
+#include "fadeout.hpp"
+#include "shrinkfade.hpp"
+#include "object/camera.hpp"
+#include "flip_level_transformer.hpp"
+#include "audio/sound_manager.hpp"
+#include "random_generator.hpp"
+
+#include "squirrel_error.hpp"
+#include "squirrel_util.hpp"
+#include "time_scheduler.hpp"
+
+namespace Scripting
+{
+
+SQInteger display(HSQUIRRELVM vm)
+{
+  Console::output << squirrel2string(vm, -1) << std::endl;
+  return 0;
+}
+
+void print_stacktrace(HSQUIRRELVM vm)
+{
+  print_squirrel_stack(vm);
+}
+
+SQInteger get_current_thread(HSQUIRRELVM vm)
+{
+  sq_pushobject(vm, vm_to_object(vm));
+  return 1;
+}
+
+void wait(HSQUIRRELVM vm, float seconds)
+{
+  TimeScheduler::instance->schedule_thread(vm, game_time + seconds);
+}
+
+void wait_for_screenswitch(HSQUIRRELVM vm)
+{
+  main_loop->waiting_threads.add(vm);
+}
+
+void exit_screen()
+{
+  main_loop->exit_screen();
+}
+
+void fadeout_screen(float seconds)
+{
+  main_loop->set_screen_fade(new FadeOut(seconds));
+}
+
+void shrink_screen(float dest_x, float dest_y, float seconds)
+{
+  main_loop->set_screen_fade(new ShrinkFade(Vector(dest_x, dest_y), seconds));
+}
+
+void abort_screenfade()
+{
+  main_loop->set_screen_fade(NULL);
+}
+
+std::string translate(const std::string& text)
+{
+  return dictionary_manager.get_dictionary().translate(text);
+}
+
+void display_text_file(const std::string& filename)
+{
+  main_loop->push_screen(new TextScroller(filename));
+}
+
+void load_worldmap(const std::string& filename)
+{
+  using namespace WorldMapNS;
+
+  main_loop->push_screen(new WorldMap(filename));
+}
+
+void load_level(const std::string& filename)
+{
+  main_loop->push_screen(new GameSession(filename));
+}
+
+static SQInteger squirrel_read_char(SQUserPointer file)
+{
+  std::istream* in = reinterpret_cast<std::istream*> (file);
+  char c = in->get();
+  if(in->eof())
+    return 0;
+
+  return c;
+}
+
+void import(HSQUIRRELVM vm, const std::string& filename)
+{
+  IFileStream in(filename);
+
+  if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in,
+          filename.c_str(), SQTrue)))
+    throw SquirrelError(vm, "Couldn't parse script");
+
+  sq_pushroottable(vm);
+  if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) {
+    sq_pop(vm, 1);
+    throw SquirrelError(vm, "Couldn't execute script");
+  }
+  sq_pop(vm, 1);
+}
+
+void debug_collrects(bool enable)
+{
+  Sector::show_collrects = enable;
+}
+
+void debug_show_fps(bool enable)
+{
+  config->show_fps = enable;
+}
+
+void debug_draw_solids_only(bool enable)
+{
+  Sector::draw_solids_only = enable;
+}
+
+void save_state()
+{
+  using namespace WorldMapNS;
+
+  if(World::current() == NULL || WorldMap::current() == NULL)
+    throw std::runtime_error("Can't save state without active World");
+
+  WorldMap::current()->save_state();
+  World::current()->save_state();
+}
+
+void update_worldmap()
+{
+  using namespace WorldMapNS;
+
+  if(WorldMap::current() == NULL)
+    throw std::runtime_error("Can't update Worldmap: none active");
+
+  WorldMap::current()->load_state();
+}
+
+// not added to header, function to only be used by others
+// in this file
+bool validate_sector_player()
+{
+  if (Sector::current() == 0)
+  {
+    log_info << "No current sector." << std::endl;
+       return false;
+  }
+
+  if (Sector::current()->player == 0)
+  {
+    log_info << "No player." << std::endl;
+       return false;
+  }
+  return true;
+}
+
+void play_music(const std::string& filename)
+{
+  sound_manager->play_music(filename);
+}
+
+void play_sound(const std::string& filename)
+{
+  sound_manager->play(filename);
+}
+
+void grease()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player; // Scripting::Player != ::Player
+  tux->physic.set_velocity_x(tux->physic.get_velocity_x()*3);
+}
+
+void invincible()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player;
+  tux->invincible_timer.start(10000);
+}
+
+void ghost()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player;
+  tux->set_ghost_mode(true);
+}
+
+void mortal()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player;
+  tux->invincible_timer.stop();
+  tux->set_ghost_mode(false);
+}
+
+void restart()
+{
+  if (GameSession::current() == 0)
+  {
+    log_info << "No game session" << std::endl;
+    return;
+  }
+  GameSession::current()->restart_level();
+}
+
+void whereami()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player;
+  log_info << "You are at x " << tux->get_pos().x << ", y " << tux->get_pos().y << std::endl;
+}
+
+void gotoend()
+{
+  if (!validate_sector_player()) return;
+  ::Player* tux = Sector::current()->player;
+  tux->move(Vector(
+          (Sector::current()->get_width()) - (SCREEN_WIDTH*2), 0));
+  Sector::current()->camera->reset(
+        Vector(tux->get_pos().x, tux->get_pos().y));
+}
+
+void camera()
+{
+  if (!validate_sector_player()) return;
+  log_info << "Camera is at " << Sector::current()->camera->get_translation().x << "," << Sector::current()->camera->get_translation().y << std::endl;
+}
+
+void quit()
+{
+  main_loop->quit();
+}
+
+int rand()
+{
+  return systemRandom.rand();
+}
+
+}
diff --git a/src/scripting/functions.hpp b/src/scripting/functions.hpp
new file mode 100644 (file)
index 0000000..d56f6c2
--- /dev/null
@@ -0,0 +1,195 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __FUNCTIONS_H__
+#define __FUNCTIONS_H__
+
+#ifndef SCRIPTING_API
+#define __suspend
+#define __custom
+#include <string>
+#include "player_status.hpp"
+#endif
+
+namespace Scripting
+{
+
+/**
+ * Display the value of the argument. This is usefull for inspecting tables.
+ */
+SQInteger display(HSQUIRRELVM vm) __custom;
+
+/**
+ * Displays contents of the current stack
+ */
+void print_stacktrace(HSQUIRRELVM vm);
+
+/**
+ * returns the currently running thread
+ */
+SQInteger get_current_thread(HSQUIRRELVM vm) __custom;
+
+/**
+ * Display a text file and scrolls it over the screen (on next screenswitch)
+ */
+void display_text_file(const std::string& filename);
+
+/**
+ * Load and display a worldmap (on next screenswitch)
+ */
+void load_worldmap(const std::string& filename);
+
+/**
+ * Load and display a level (on next screenswitch)
+ */
+void load_level(const std::string& filename);
+
+/**
+ * Suspend the script execution for the specified number of seconds
+ */
+void wait(HSQUIRRELVM vm, float seconds) __suspend;
+
+/**
+ * Suspend the script execution until the current screen has been changed
+ */
+void wait_for_screenswitch(HSQUIRRELVM vm) __suspend;
+
+/**
+ * Exits the currently running screen (force exit from worldmap or scrolling
+ * text for example)
+ */
+void exit_screen();
+
+/**
+ * Does a fadeout for the specified number of seconds before next screenchange
+ */
+void fadeout_screen(float seconds);
+
+/**
+ * Does a shrinking fade towards the destposition for the specified number of
+ * seconds before next screenchange
+ */
+void shrink_screen(float dest_x, float dest_y, float seconds);
+
+/**
+ * Aborts any kind of previous screen fade; the screenchange will happen
+ * anyway.
+ */
+void abort_screenfade();
+
+/**
+ * Translate a text into the users language (by looking it up in the .po
+ * files)
+ */
+std::string translate(const std::string& text);
+
+/**
+ * Load a script file and executes it. This is typically used to import
+ * functions from external files.
+ */
+void import(HSQUIRRELVM v, const std::string& filename);
+
+/**
+ * Save world state to savegame
+ */
+void save_state();
+
+/**
+ * Update worldmap from worldmap state (state.world variable)
+ */
+void update_worldmap();
+
+/**
+ * enable/disable drawing of collision rectangles
+ */
+void debug_collrects(bool enable);
+
+/**
+ * enable/disable drawing of fps
+ */
+void debug_show_fps(bool enable);
+
+/**
+ * enable/disable drawing of non-solid layers
+ */
+void debug_draw_solids_only(bool enable);
+
+/**
+ * Changes music to musicfile
+ */
+void play_music(const std::string& musicfile);
+
+/**
+ * Plays a soundfile
+ */
+void play_sound(const std::string& soundfile);
+
+/**
+ * speeds Tux up
+ */
+void grease();
+
+/**
+ * makes Tux invincible for 10000 units of time
+ */
+void invincible();
+
+/**
+ * makes Tux a ghost, i.e. lets him float around and through solid objects
+ */
+void ghost();
+
+/**
+ * recall Tux's invincibility and ghost status
+ */
+void mortal();
+
+/**
+ * reinitialise and respawn Tux at the beginning of the current level
+ */
+void restart();
+
+/**
+ * print Tux's current coordinates in a level
+ */
+void whereami();
+
+/**
+ * move Tux near the end of the level
+ */
+void gotoend();
+
+/**
+ * show the camera's coordinates
+ */
+void camera();
+
+/**
+ * exit the game
+ */
+void quit();
+
+/**
+ * Returns a random integer
+ */
+int rand();
+
+}
+
+#endif
diff --git a/src/scripting/level.cpp b/src/scripting/level.cpp
new file mode 100644 (file)
index 0000000..f780ee2
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "level.hpp"
+#include "game_session.hpp"
+#include "flip_level_transformer.hpp"
+
+namespace Scripting
+{
+  Level::Level()
+  {}
+
+  Level::~Level()
+  {}
+
+  void
+  Level::finish(bool win)
+  {
+    if(GameSession::current() == NULL)
+      return;
+
+    GameSession::current()->finish(win);
+  }
+
+  void
+  Level::spawn(const std::string& sector, const std::string& spawnpoint)
+  {
+    if(GameSession::current() == NULL)
+      return;
+
+    GameSession::current()->respawn(sector, spawnpoint);
+  }
+
+  void
+  Level::flip_vertically()
+  {
+    FlipLevelTransformer flip_transformer;
+    flip_transformer.transform(GameSession::current()->get_current_level());
+  }
+
+  void
+  Level::toggle_pause()
+  {
+    if(GameSession::current() == NULL)
+      return;
+    GameSession::current()->toggle_pause();
+  }
+}
diff --git a/src/scripting/level.hpp b/src/scripting/level.hpp
new file mode 100644 (file)
index 0000000..2cdc258
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __LEVEL_H__
+#define __LEVEL_H__
+
+namespace Scripting
+{
+
+class Level
+{
+public:
+#ifndef SCRIPTING_API
+    Level();
+    ~Level();
+#endif
+
+    /** Instantly finish the currently played level */
+    void finish(bool win);
+    /** spawn tux at specified sector and spawnpoint */
+    void spawn(const std::string& sector, const std::string& spawnpoint);
+    /** Flip level vertically */
+    void flip_vertically();
+    /** toggle pause */
+    void toggle_pause();
+};
+
+}
+
+#endif
diff --git a/src/scripting/level_time.cpp b/src/scripting/level_time.cpp
new file mode 100644 (file)
index 0000000..865681c
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/level_time.hpp"
+#include "scripting/level_time.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  LevelTime::LevelTime(::LevelTime* level_time)
+    : level_time(level_time)
+  { }
+
+  LevelTime::~LevelTime()
+  { }
+
+  void LevelTime::start()
+  {
+    level_time->start();
+  }
+
+  void LevelTime::stop()
+  {
+    level_time->stop();
+  }
+
+  float LevelTime::get_time()
+  {
+    return level_time->get_time();
+  }
+
+  void LevelTime::set_time(float time_left)
+  {
+    level_time->set_time(time_left);
+  }
+
+}
diff --git a/src/scripting/level_time.hpp b/src/scripting/level_time.hpp
new file mode 100644 (file)
index 0000000..39b2b58
--- /dev/null
@@ -0,0 +1,66 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_LEVELTIME_H__
+#define __SCRIPTING_LEVELTIME_H__
+
+#ifndef SCRIPTING_API
+class LevelTime;
+typedef LevelTime _LevelTime;
+#endif
+
+namespace Scripting
+{
+
+class LevelTime
+{
+public:
+#ifndef SCRIPTING_API
+  LevelTime(_LevelTime* level_time);
+  ~LevelTime();
+#endif
+
+  /**
+   * Resumes the countdown
+   */
+  void start();
+
+  /**
+   * Pauses the countdown
+   */
+  void stop();
+
+  /**
+   * Returns the number of seconds left on the clock
+   */
+  float get_time();
+
+  /**
+   * Changes the number of seconds left on the clock
+   */
+  void set_time(float time_left);
+
+#ifndef SCRIPTING_API
+  _LevelTime* level_time;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/platform.cpp b/src/scripting/platform.cpp
new file mode 100644 (file)
index 0000000..fa44f2b
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/platform.hpp"
+#include "scripting/platform.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  Platform::Platform(::Platform* platform)
+    : platform(platform)
+  { }
+
+  Platform::~Platform()
+  { }
+
+  void Platform::goto_node(int node_no)
+  {
+    platform->goto_node(node_no);
+  }
+
+  void Platform::start_moving()
+  {
+    platform->start_moving();
+  }
+
+  void Platform::stop_moving()
+  {
+    platform->stop_moving();
+  }
+
+}
diff --git a/src/scripting/platform.hpp b/src/scripting/platform.hpp
new file mode 100644 (file)
index 0000000..7d85f33
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_PLATFORM_H__
+#define __SCRIPTING_PLATFORM_H__
+
+#ifndef SCRIPTING_API
+class Platform;
+typedef Platform _Platform;
+#endif
+
+namespace Scripting
+{
+
+class Platform
+{
+public:
+#ifndef SCRIPTING_API
+  Platform(_Platform* platform);
+  ~Platform();
+#endif
+
+  /** Move platform until at given node, then stop */
+  void goto_node(int node_no);
+
+  /** Start moving platform */
+  void start_moving();
+
+  /** Stop platform at next node */
+  void stop_moving();
+
+#ifndef SCRIPTING_API
+  _Platform* platform;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/player.hpp b/src/scripting/player.hpp
new file mode 100644 (file)
index 0000000..5fe8713
--- /dev/null
@@ -0,0 +1,123 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_PLAYER_H__
+#define __SCRIPTING_PLAYER_H__
+
+namespace Scripting
+{
+
+class Player
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~Player()
+  {}
+#endif
+
+  /**
+   * Set tux bonus.
+   * This can be "grow", "fireflower" or "iceflower" at the moment
+   */
+  virtual bool add_bonus(const std::string& bonus) = 0;
+  /**
+   * Give tux more coins
+   */
+  virtual void add_coins(int count) = 0;
+  /**
+   * Make tux invicible for a short amount of time
+   */
+  virtual void make_invincible() = 0;
+  /**
+   * Deactivate user input for Tux
+   */
+  virtual void deactivate() = 0;
+  /**
+   * Give control back to user
+   */
+  virtual void activate() = 0;
+  /**
+   * Make Tux walk
+   */
+  virtual void walk(float speed) = 0;
+  /**
+   * Set player visible or not visible
+   */
+  virtual void set_visible(bool visible) = 0;
+  /**
+   * returns true if the player is currently visible (that is he was not set
+   * inivisible by the set_visible method)
+   */
+  virtual bool get_visible() = 0;
+
+  /**
+   * Hurts a player, if completely=true then the player will be killed even
+   * if he had grow or fireflower bonus
+   */
+  virtual void kill(bool completely) = 0;
+
+  /**
+   * Switches ghost mode on/off.
+   * Lets Tux float around and through solid objects.
+   */
+  virtual void set_ghost_mode(bool enable) = 0;
+
+  /**
+   * Returns whether ghost mode is currently enabled
+   */
+  virtual bool get_ghost_mode() = 0;
+
+  /**
+   * play cheer animation.
+   * This might need some space and behave in an unpredictable way. Best to use this at level end.
+   */
+  virtual void do_cheer() = 0;
+
+  /**
+   * duck down if possible.
+   * this won't last long as long as input is enabled.
+   */
+  virtual void do_duck() = 0;
+
+  /**
+   * stand back up if possible.
+   */
+  virtual void do_standup() = 0;
+
+  /**
+   * do a backflip if possible.
+   */
+  virtual void do_backflip() = 0;
+
+  /**
+   * jump in the air if possible
+   * sensible values for yspeed are negative - unless we want to jump into the ground of course
+   */
+  virtual void do_jump(float yspeed) = 0;
+
+  /**
+   * Orders the current GameSession to start a sequence
+   */
+  virtual void trigger_sequence(std::string sequence_name) = 0;
+
+};
+
+}
+
+#endif
diff --git a/src/scripting/scripted_object.hpp b/src/scripting/scripted_object.hpp
new file mode 100644 (file)
index 0000000..c398443
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTED_OBJECT_INTERFACE_H__
+#define __SCRIPTED_OBJECT_INTERFACE_H__
+
+namespace Scripting
+{
+
+class ScriptedObject
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~ScriptedObject()
+  {}
+#endif
+
+  virtual void set_action(const std::string& animation) = 0;
+  virtual std::string get_action() = 0;
+
+  virtual void move(float x, float y) = 0;
+  virtual void set_pos(float x, float y) = 0;
+  virtual float get_pos_x() = 0;
+  virtual float get_pos_y() = 0;
+
+  virtual void set_velocity(float x, float y) = 0;
+  virtual float get_velocity_x() = 0;
+  virtual float get_velocity_y() = 0;
+
+  virtual void set_visible(bool visible) = 0;
+  virtual bool is_visible() = 0;
+
+  virtual void set_solid(bool solid) = 0;
+  virtual bool is_solid() = 0;
+
+  virtual std::string get_name() = 0;
+};
+
+}
+
+#endif
diff --git a/src/scripting/serialize.cpp b/src/scripting/serialize.cpp
new file mode 100644 (file)
index 0000000..3455d2f
--- /dev/null
@@ -0,0 +1,138 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "serialize.hpp"
+
+#include <memory>
+#include <assert.h>
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/writer.hpp"
+#include "squirrel_error.hpp"
+
+namespace Scripting
+{
+
+void load_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, const lisp::Lisp* lisp)
+{
+  using namespace lisp;
+
+  if(table_idx < 0)
+    table_idx -= 2;
+
+  lisp::ListIterator iter(lisp);
+  while(iter.next() && iter.lisp() != NULL) {
+    const std::string& token = iter.item();
+    sq_pushstring(vm, token.c_str(), token.size());
+
+    const lisp::Lisp* value = iter.value();
+    switch(value->get_type()) {
+      case Lisp::TYPE_CONS:
+        sq_newtable(vm);
+        load_squirrel_table(vm, sq_gettop(vm), iter.lisp());
+        break;
+      case Lisp::TYPE_INTEGER:
+        sq_pushinteger(vm, value->get_int());
+        break;
+      case Lisp::TYPE_REAL:
+        sq_pushfloat(vm, value->get_float());
+        break;
+      case Lisp::TYPE_STRING:
+        sq_pushstring(vm, value->get_string().c_str(), -1);
+        break;
+      case Lisp::TYPE_BOOLEAN:
+        sq_pushbool(vm, value->get_bool() ? SQTrue : SQFalse);
+        break;
+      case Lisp::TYPE_SYMBOL:
+        std::cerr << "Unexpected symbol in lisp file...";
+        sq_pushnull(vm);
+        break;
+      default:
+        assert(false);
+        break;
+    }
+
+    if(SQ_FAILED(sq_createslot(vm, table_idx)))
+      throw Scripting::SquirrelError(vm, "Couldn't create new index");
+  }
+}
+
+void save_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, lisp::Writer& writer)
+{
+  // offset because of sq_pushnull
+  if(table_idx < 0)
+    table_idx -= 1;
+
+  //iterator table
+  sq_pushnull(vm);
+  while(SQ_SUCCEEDED(sq_next(vm, table_idx))) {
+    if(sq_gettype(vm, -2) != OT_STRING) {
+      std::cerr << "Table contains non-string key\n";
+      continue;
+    }
+    const SQChar* key;
+    sq_getstring(vm, -2, &key);
+
+    switch(sq_gettype(vm, -1)) {
+      case OT_INTEGER: {
+        SQInteger val;
+        sq_getinteger(vm, -1, &val);
+        writer.write_int(key, static_cast<int> (val));
+        break;
+      }
+      case OT_FLOAT: {
+        SQFloat val;
+        sq_getfloat(vm, -1, &val);
+        writer.write_float(key, static_cast<float> (val));
+        break;
+      }
+      case OT_BOOL: {
+        SQBool val;
+        sq_getbool(vm, -1, &val);
+        writer.write_bool(key, val == SQTrue);
+        break;
+      }
+      case OT_STRING: {
+        const SQChar* str;
+        sq_getstring(vm, -1, &str);
+        writer.write_string(key, reinterpret_cast<const char*> (str));
+        break;
+      }
+      case OT_TABLE: {
+        writer.start_list(key, true);
+        save_squirrel_table(vm, -1, writer);
+        writer.end_list(key);
+        break;
+      }
+      case OT_CLOSURE:
+        break; // ignore
+      case OT_NATIVECLOSURE:
+        break;
+      default:
+        std::cerr << "Can't serialize key '" << key << "' in table.\n";
+        break;
+    }
+    sq_pop(vm, 2);
+  }
+  sq_pop(vm, 1);
+}
+
+}
diff --git a/src/scripting/serialize.hpp b/src/scripting/serialize.hpp
new file mode 100644 (file)
index 0000000..d34b00f
--- /dev/null
@@ -0,0 +1,36 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SERIALIZE_HPP__
+#define __SERIALIZE_HPP__
+
+#include <squirrel.h>
+#include <string>
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+
+namespace Scripting
+{
+
+  void save_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, lisp::Writer& writer);
+  void load_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, const lisp::Lisp* lisp);
+
+}
+
+#endif
diff --git a/src/scripting/squirrel_error.cpp b/src/scripting/squirrel_error.cpp
new file mode 100644 (file)
index 0000000..2c28273
--- /dev/null
@@ -0,0 +1,55 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "squirrel_error.hpp"
+#include <sstream>
+
+namespace Scripting
+{
+
+SquirrelError::SquirrelError(HSQUIRRELVM v, const std::string& message) throw()
+{
+  std::ostringstream msg;
+  msg << "Squirrel error: " << message << " (";
+  const char* lasterr;
+  sq_getlasterror(v);
+  if(sq_gettype(v, -1) != OT_STRING)
+  {
+    lasterr = "no error info";
+  }
+  else
+  {
+    sq_getstring(v, -1, &lasterr);
+  }
+  msg << lasterr << ")";
+  sq_pop(v, 1);
+  this->message = msg.str();
+}
+
+SquirrelError::~SquirrelError() throw()
+{}
+
+const char*
+SquirrelError::what() const throw()
+{
+  return message.c_str();
+}
+
+}
diff --git a/src/scripting/squirrel_error.hpp b/src/scripting/squirrel_error.hpp
new file mode 100644 (file)
index 0000000..5e64b1d
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SQUIRREL_ERROR_HPP__
+#define __SQUIRREL_ERROR_HPP__
+
+#include <squirrel.h>
+#include <stdexcept>
+
+namespace Scripting
+{
+
+/** Exception class for squirrel errors, it takes a squirrelvm and uses
+ * sq_geterror() to retrieve additional information about the last error that
+ * occured and creates a readable message from that.
+ */
+class SquirrelError : public std::exception
+{
+public:
+  SquirrelError(HSQUIRRELVM v, const std::string& message) throw();
+  virtual ~SquirrelError() throw();
+
+  const char* what() const throw();
+private:
+  std::string message;
+};
+
+}
+
+#endif
diff --git a/src/scripting/squirrel_util.cpp b/src/scripting/squirrel_util.cpp
new file mode 100644 (file)
index 0000000..5f2e3f3
--- /dev/null
@@ -0,0 +1,556 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <stdarg.h>
+#include <squirrel.h>
+#include <sqstdmath.h>
+#include <sqstdblob.h>
+#include <sqstdstring.h>
+#include <sqstdaux.h>
+#include <sqstdio.h>
+#include "squirrel_util.hpp"
+#include "log.hpp"
+#include "level.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "../random_generator.hpp"
+
+#ifdef ENABLE_SQDBG
+#include <sqdbg/sqrdbg.h>
+
+static HSQREMOTEDBG debugger = NULL;
+#endif
+
+namespace Scripting
+{
+
+HSQUIRRELVM global_vm = NULL;
+
+static void printfunc(HSQUIRRELVM, const char* str, ...)
+{
+  char buf[4096];
+  va_list arglist;
+  va_start(arglist, str);
+  vsprintf(buf, str, arglist);
+  Console::output << (const char*) buf << std::flush;
+  va_end(arglist);
+}
+
+void init_squirrel(bool enable_debugger)
+{
+  global_vm = sq_open(64);
+  if(global_vm == NULL)
+    throw std::runtime_error("Couldn't initialize squirrel vm");
+
+  if(enable_debugger) {
+#ifdef ENABLE_SQDBG
+    sq_enabledebuginfo(global_vm, SQTrue);
+    debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
+    if(debugger == NULL)
+      throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
+
+    sq_enabledebuginfo(global_vm, SQTrue);
+    log_info << "Waiting for debug client..." << std::endl;
+    if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
+      throw SquirrelError(global_vm, "Waiting for debug clients failed");
+    log_info << "debug client connected." << std::endl;
+#endif
+  }
+
+  sq_pushroottable(global_vm);
+  if(SQ_FAILED(sqstd_register_bloblib(global_vm)))
+    throw SquirrelError(global_vm, "Couldn't register blob lib");
+  if(SQ_FAILED(sqstd_register_mathlib(global_vm)))
+    throw SquirrelError(global_vm, "Couldn't register math lib");
+  if(SQ_FAILED(sqstd_register_stringlib(global_vm)))
+    throw SquirrelError(global_vm, "Couldn't register string lib");
+
+  // remove rand and srand calls from sqstdmath, we'll provide our own
+  sq_pushstring(global_vm, "srand", -1);
+  sq_deleteslot(global_vm, -2, SQFalse);
+  sq_pushstring(global_vm, "rand", -1);
+  sq_deleteslot(global_vm, -2, SQFalse);
+
+  // register supertux API
+  register_supertux_wrapper(global_vm);
+
+  // TODO remove this at some point... it shoud just be functions not an object
+  expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
+
+  sq_pop(global_vm, 1);
+
+  // register print function
+  sq_setprintfunc(global_vm, printfunc);
+  // register default error handlers
+  sqstd_seterrorhandlers(global_vm);
+
+  // try to load default script
+  try {
+    std::string filename = "scripts/default.nut";
+    IFileStream stream(filename);
+    Scripting::compile_and_run(global_vm, stream, filename);
+  } catch(std::exception& e) {
+    log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
+  }
+}
+
+void exit_squirrel()
+{
+#ifdef ENABLE_SQDBG
+  if(debugger != NULL) {
+    sq_rdbg_shutdown(debugger);
+    debugger = NULL;
+  }
+#endif
+
+  if (global_vm)
+    sq_close(global_vm);
+
+  global_vm = NULL;
+}
+
+void update_debugger()
+{
+#ifdef ENABLE_SQDBG
+  if(debugger != NULL)
+    sq_rdbg_update(debugger);
+#endif
+}
+
+std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
+{
+  std::ostringstream os;
+  switch(sq_gettype(v, i))
+    {
+    case OT_NULL:
+      os << "<null>";
+      break;
+    case OT_BOOL: {
+      SQBool p;
+      sq_getbool(v, i, &p);
+      if (p)
+        os << "true";
+      else
+        os << "false";
+      break;
+    }
+    case OT_INTEGER: {
+      SQInteger val;
+      sq_getinteger(v, i, &val);
+      os << val;
+      break;
+    }
+    case OT_FLOAT: {
+      SQFloat val;
+      sq_getfloat(v, i, &val);
+      os << val;
+      break;
+    }
+    case OT_STRING: {
+      const SQChar* val;
+      sq_getstring(v, i, &val);
+      os << "\"" << val << "\"";
+      break;
+    }
+    case OT_TABLE: {
+      bool first = true;
+      os << "{";
+      sq_pushnull(v);  //null iterator
+      while(SQ_SUCCEEDED(sq_next(v,i-1)))
+        {
+          if (!first) {
+            os << ", ";
+          }
+          first = false;
+
+          //here -1 is the value and -2 is the key
+          os << squirrel2string(v, -2) << " => "
+             << squirrel2string(v, -1);
+
+          sq_pop(v,2); //pops key and val before the nex iteration
+        }
+      sq_pop(v, 1);
+      os << "}";
+      break;
+    }
+    case OT_ARRAY: {
+      bool first = true;
+      os << "[";
+      sq_pushnull(v);  //null iterator
+      while(SQ_SUCCEEDED(sq_next(v,i-1)))
+        {
+          if (!first) {
+            os << ", ";
+          }
+          first = false;
+
+          //here -1 is the value and -2 is the key
+          // we ignore the key, since that is just the index in an array
+          os << squirrel2string(v, -1);
+
+          sq_pop(v,2); //pops key and val before the nex iteration
+        }
+      sq_pop(v, 1);
+      os << "]";
+      break;
+    }
+    case OT_USERDATA:
+      os << "<userdata>";
+      break;
+    case OT_CLOSURE:
+      os << "<closure>";
+      break;
+    case OT_NATIVECLOSURE:
+      os << "<native closure>";
+      break;
+    case OT_GENERATOR:
+      os << "<generator>";
+      break;
+    case OT_USERPOINTER:
+      os << "userpointer";
+      break;
+    case OT_THREAD:
+      os << "<thread>";
+      break;
+    case OT_CLASS:
+      os << "<class>";
+      break;
+    case OT_INSTANCE:
+      os << "<instance>";
+      break;
+    case OT_WEAKREF:
+      os << "<weakref>";
+      break;
+    default:
+      os << "<unknown>";
+      break;
+    }
+  return os.str();
+}
+
+void print_squirrel_stack(HSQUIRRELVM v)
+{
+    printf("--------------------------------------------------------------\n");
+    int count = sq_gettop(v);
+    for(int i = 1; i <= count; ++i) {
+        printf("%d: ",i);
+        switch(sq_gettype(v, i))
+        {
+            case OT_NULL:
+                printf("null");
+                break;
+            case OT_INTEGER: {
+                SQInteger val;
+                sq_getinteger(v, i, &val);
+                printf("integer (%d)", static_cast<int> (val));
+                break;
+            }
+            case OT_FLOAT: {
+                SQFloat val;
+                sq_getfloat(v, i, &val);
+                printf("float (%f)", val);
+                break;
+            }
+            case OT_STRING: {
+                const SQChar* val;
+                sq_getstring(v, i, &val);
+                printf("string (%s)", val);
+                break;
+            }
+            case OT_TABLE:
+                printf("table");
+                break;
+            case OT_ARRAY:
+                printf("array");
+                break;
+            case OT_USERDATA:
+                printf("userdata");
+                break;
+            case OT_CLOSURE:
+                printf("closure(function)");
+                break;
+            case OT_NATIVECLOSURE:
+                printf("native closure(C function)");
+                break;
+            case OT_GENERATOR:
+                printf("generator");
+                break;
+            case OT_USERPOINTER:
+                printf("userpointer");
+                break;
+            case OT_THREAD:
+                printf("thread");
+                break;
+            case OT_CLASS:
+                printf("class");
+                break;
+            case OT_INSTANCE:
+                printf("instance");
+                break;
+            case OT_WEAKREF:
+                printf("weakref");
+                break;
+            default:
+                printf("unknown?!?");
+                break;
+        }
+        printf("\n");
+    }
+    printf("--------------------------------------------------------------\n");
+}
+
+static SQInteger squirrel_read_char(SQUserPointer file)
+{
+  std::istream* in = reinterpret_cast<std::istream*> (file);
+  char c = in->get();
+  if(in->eof())
+    return 0;
+  return c;
+}
+
+void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
+{
+  if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
+    throw SquirrelError(vm, "Couldn't parse script");
+}
+
+void compile_and_run(HSQUIRRELVM vm, std::istream& in,
+                     const std::string& sourcename)
+{
+  compile_script(vm, in, sourcename);
+
+  SQInteger oldtop = sq_gettop(vm);
+
+  try {
+    sq_pushroottable(vm);
+    if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
+      throw SquirrelError(vm, "Couldn't start script");
+  } catch(...) {
+    sq_settop(vm, oldtop);
+    throw;
+  }
+
+  // we can remove the closure in case the script was not suspended
+  if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+    sq_settop(vm, oldtop-1);
+  }
+}
+
+HSQOBJECT create_thread(HSQUIRRELVM vm)
+{
+  HSQUIRRELVM new_vm = sq_newthread(vm, 64);
+  if(new_vm == NULL)
+    throw SquirrelError(vm, "Couldn't create new VM");
+
+  HSQOBJECT vm_object;
+  sq_resetobject(&vm_object);
+  if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
+    throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
+  sq_addref(vm, &vm_object);
+
+  sq_pop(vm, 1);
+
+  return vm_object;
+}
+
+HSQOBJECT vm_to_object(HSQUIRRELVM vm)
+{
+  HSQOBJECT object;
+  sq_resetobject(&object);
+  object._unVal.pThread = vm;
+  object._type = OT_THREAD;
+
+  return object;
+}
+
+HSQUIRRELVM object_to_vm(HSQOBJECT object)
+{
+  if(object._type != OT_THREAD)
+    return NULL;
+
+  return object._unVal.pThread;
+}
+
+// begin: serialization functions
+
+void store_float(HSQUIRRELVM vm, const char* name, float val)
+{
+  sq_pushstring(vm, name, -1);
+  sq_pushfloat(vm, val);
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+void store_int(HSQUIRRELVM vm, const char* name, int val)
+{
+  sq_pushstring(vm, name, -1);
+  sq_pushinteger(vm, val);
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't add int value to table");
+}
+
+void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
+{
+  sq_pushstring(vm, name, -1);
+  sq_pushstring(vm, val.c_str(), val.length());
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+void store_bool(HSQUIRRELVM vm, const char* name, bool val)
+{
+  sq_pushstring(vm, name, -1);
+  sq_pushbool(vm, val ? SQTrue : SQFalse);
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+bool has_float(HSQUIRRELVM vm, const char* name)
+{
+  sq_pushstring(vm, name, -1);
+  if (SQ_FAILED(sq_get(vm, -2))) return false;
+  sq_pop(vm, 1);
+  return true;
+}
+
+bool has_int(HSQUIRRELVM vm, const char* name)
+{
+  return has_float(vm, name);
+}
+
+bool has_string(HSQUIRRELVM vm, const char* name)
+{
+  return has_float(vm, name);
+}
+
+bool has_bool(HSQUIRRELVM vm, const char* name)
+{
+  return has_float(vm, name);
+}
+
+float read_float(HSQUIRRELVM vm, const char* name)
+{
+  sq_pushstring(vm, name, -1);
+  if(SQ_FAILED(sq_get(vm, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't get float value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+
+  float result;
+  if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
+    std::ostringstream msg;
+    msg << "Couldn't get float value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+  sq_pop(vm, 1);
+
+  return result;
+}
+
+int read_int(HSQUIRRELVM vm, const char* name)
+{
+  sq_pushstring(vm, name, -1);
+  if(SQ_FAILED(sq_get(vm, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't get int value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+
+  SQInteger result;
+  if(SQ_FAILED(sq_getinteger(vm, -1, &result))) {
+    std::ostringstream msg;
+    msg << "Couldn't get int value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+  sq_pop(vm, 1);
+
+  return result;
+}
+
+
+std::string read_string(HSQUIRRELVM vm, const char* name)
+{
+  sq_pushstring(vm, name, -1);
+  if(SQ_FAILED(sq_get(vm, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't get string value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+
+  const char* result;
+  if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
+    std::ostringstream msg;
+    msg << "Couldn't get string value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+  sq_pop(vm, 1);
+
+  return std::string(result);
+}
+
+bool read_bool(HSQUIRRELVM vm, const char* name)
+{
+  sq_pushstring(vm, name, -1);
+  if(SQ_FAILED(sq_get(vm, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't get bool value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+
+  SQBool result;
+  if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
+    std::ostringstream msg;
+    msg << "Couldn't get bool value for '" << name << "' from table";
+    throw Scripting::SquirrelError(vm, msg.str());
+  }
+  sq_pop(vm, 1);
+
+  return result == SQTrue;
+}
+
+bool get_float(HSQUIRRELVM vm, const char* name, float& val) {
+  if (!has_float(vm, name)) return false;
+  val = read_float(vm, name);
+  return true;
+}
+
+bool get_int(HSQUIRRELVM vm, const char* name, int& val) {
+  if (!has_int(vm, name)) return false;
+  val = read_int(vm, name);
+  return true;
+}
+
+bool get_string(HSQUIRRELVM vm, const char* name, std::string& val) {
+  if (!has_string(vm, name)) return false;
+  val = read_string(vm, name);
+  return true;
+}
+
+bool get_bool(HSQUIRRELVM vm, const char* name, bool& val) {
+  if (!has_bool(vm, name)) return false;
+  val = read_bool(vm, name);
+  return true;
+}
+
+// end: serialization functions
+
+}
diff --git a/src/scripting/squirrel_util.hpp b/src/scripting/squirrel_util.hpp
new file mode 100644 (file)
index 0000000..da61b26
--- /dev/null
@@ -0,0 +1,107 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SQUIRREL_UTIL_HPP__
+#define __SQUIRREL_UTIL_HPP__
+
+#include <squirrel.h>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include "wrapper.hpp"
+#include "squirrel_error.hpp"
+
+namespace Scripting
+{
+
+  extern HSQUIRRELVM global_vm;
+
+  void init_squirrel(bool enable_debugger);
+  void exit_squirrel();
+  void update_debugger();
+
+  std::string squirrel2string(HSQUIRRELVM vm, SQInteger i);
+  void print_squirrel_stack(HSQUIRRELVM vm);
+
+  HSQOBJECT create_thread(HSQUIRRELVM vm);
+  SQObject vm_to_object(HSQUIRRELVM vm);
+  HSQUIRRELVM object_to_vm(HSQOBJECT object);
+
+  void compile_script(HSQUIRRELVM vm, std::istream& in,
+                      const std::string& sourcename);
+  void compile_and_run(HSQUIRRELVM vm, std::istream& in,
+                       const std::string& sourcename);
+
+  template<typename T>
+  void expose_object(HSQUIRRELVM v, SQInteger table_idx, T* object,
+                     const std::string& name, bool free = false)
+  {
+    sq_pushstring(v, name.c_str(), -1);
+    Scripting::create_squirrel_instance(v, object, free);
+
+    if(table_idx < 0)
+      table_idx -= 2;
+
+    // register instance in root table
+    if(SQ_FAILED(sq_createslot(v, table_idx))) {
+      std::ostringstream msg;
+      msg << "Couldn't register object '" << name << "' in squirrel table";
+      throw Scripting::SquirrelError(v, msg.str());
+    }
+  }
+
+  static inline void unexpose_object(HSQUIRRELVM v, SQInteger table_idx,
+                                     const std::string& name)
+  {
+    sq_pushstring(v, name.c_str(), name.length());
+
+    if(table_idx < 0)
+      table_idx -= 1;
+
+    if(SQ_FAILED(sq_deleteslot(v, table_idx, SQFalse))) {
+      std::ostringstream msg;
+      msg << "Couldn't unregister object '" << name << "' in squirrel root table";
+      throw Scripting::SquirrelError(v, msg.str());
+    }
+  }
+
+  // begin serialization functions
+  void store_float(HSQUIRRELVM vm, const char* name, float val);
+  void store_int(HSQUIRRELVM vm, const char* name, int val);
+  void store_string(HSQUIRRELVM vm, const char* name, const std::string& val);
+  void store_bool(HSQUIRRELVM vm, const char* name, bool val);
+
+  bool has_float(HSQUIRRELVM vm, const char* name);
+  bool has_int(HSQUIRRELVM vm, const char* name);
+  bool has_string(HSQUIRRELVM vm, const char* name);
+  bool has_bool(HSQUIRRELVM vm, const char* name);
+
+  bool get_float(HSQUIRRELVM vm, const char* name, float& val);
+  bool get_int(HSQUIRRELVM vm, const char* name, int& val);
+  bool get_string(HSQUIRRELVM vm, const char* name, std::string& val);
+  bool get_bool(HSQUIRRELVM vm, const char* name, bool& val);
+
+  float read_float(HSQUIRRELVM vm, const char* name);
+  int read_int(HSQUIRRELVM vm, const char* name);
+  std::string read_string(HSQUIRRELVM vm, const char* name);
+  bool read_bool(HSQUIRRELVM vm, const char* name);
+  // end serialization functions
+
+}
+
+#endif
diff --git a/src/scripting/ssector.hpp b/src/scripting/ssector.hpp
new file mode 100644 (file)
index 0000000..82fc542
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux - Sector Scripting
+//  Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SECTOR_H__
+#define __SECTOR_H__
+
+namespace Scripting
+{
+
+class SSector
+{
+public:
+#ifndef SCRIPTING_API
+    virtual ~SSector()
+    {}
+#endif
+  virtual void set_ambient_light(float red, float green, float blue) = 0;
+  virtual float get_ambient_red() = 0;
+  virtual float get_ambient_green() = 0;
+  virtual float get_ambient_blue() = 0;
+};
+
+}
+
+#endif
diff --git a/src/scripting/text.hpp b/src/scripting/text.hpp
new file mode 100644 (file)
index 0000000..89ff0f3
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __TEXT_H__
+#define __TEXT_H__
+
+namespace Scripting
+{
+
+class Text
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~Text()
+  { }
+#endif
+
+  virtual void set_text(const std::string& text) = 0;
+  virtual void set_font(const std::string& fontname) = 0;
+  virtual void fade_in(float fadetime) = 0;
+  virtual void fade_out(float fadetime) = 0;
+  virtual void set_visible(bool visible) = 0;
+  virtual void set_centered(bool centered) = 0;
+  virtual void set_pos(float x, float y) = 0;
+  virtual float get_pos_x() = 0;
+  virtual float get_pos_y() = 0;
+  virtual void set_anchor_point(int anchor) = 0;
+  virtual int  get_anchor_point() = 0;
+};
+
+}
+
+#endif
diff --git a/src/scripting/thread_queue.cpp b/src/scripting/thread_queue.cpp
new file mode 100644 (file)
index 0000000..6ecaef3
--- /dev/null
@@ -0,0 +1,86 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "thread_queue.hpp"
+#include "squirrel_util.hpp"
+#include "log.hpp"
+
+namespace Scripting
+{
+
+ThreadQueue::ThreadQueue()
+{
+}
+
+ThreadQueue::~ThreadQueue()
+{
+}
+
+void
+ThreadQueue::add(HSQUIRRELVM vm)
+{
+  // create a weakref to the VM
+  HSQOBJECT vm_obj = vm_to_object(vm);
+  sq_pushobject(global_vm, vm_obj);
+  sq_weakref(global_vm, -1);
+
+  HSQOBJECT object;
+  if(SQ_FAILED(sq_getstackobj(global_vm, -1, &object))) {
+    sq_pop(global_vm, 2);
+    throw SquirrelError(global_vm, "Couldn't get thread weakref from vm");
+  }
+  sq_addref(global_vm, &object);
+  threads.push_back(object);
+
+  sq_pop(global_vm, 2);
+}
+
+void
+ThreadQueue::wakeup()
+{
+  // we traverse the list in reverse orders and use indices. This should be
+  // robust for scripts that add new entries to the list while we're traversing
+  // it
+  size_t i = threads.size() - 1;
+  size_t end = (size_t) 0 - 1;
+  size_t size_begin = threads.size();
+  while(i != end) {
+    HSQOBJECT object = threads[i];
+
+    sq_pushobject(global_vm, object);
+    sq_getweakrefval(global_vm, -1);
+
+    HSQUIRRELVM scheduled_vm;
+    if(sq_gettype(global_vm, -1) == OT_THREAD &&
+            SQ_SUCCEEDED(sq_getthread(global_vm, -1, &scheduled_vm))) {
+      if(SQ_FAILED(sq_wakeupvm(scheduled_vm, SQFalse, SQFalse, SQTrue))) {
+        log_warning << "Couldn't wakeup scheduled squirrel VM" << std::endl;
+      }
+    }
+
+    sq_release(global_vm, &object);
+    sq_pop(global_vm, 1);
+    i--;
+  }
+
+  threads.erase(threads.begin(), threads.begin() + size_begin);
+}
+
+}
diff --git a/src/scripting/thread_queue.hpp b/src/scripting/thread_queue.hpp
new file mode 100644 (file)
index 0000000..ff0e651
--- /dev/null
@@ -0,0 +1,49 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __THREAD_QUEUE_HPP__
+#define __THREAD_QUEUE_HPP__
+
+#include <vector>
+#include <squirrel.h>
+
+namespace Scripting
+{
+
+/**
+ * Keeps a list of SquirrelThreads that wait for a wakeup event
+ */
+class ThreadQueue
+{
+public:
+  ThreadQueue();
+  virtual ~ThreadQueue();
+
+  /// adds a thread (actually a weakref to the thread)
+  void add(HSQUIRRELVM vm);
+  /// wakes up threads in the list
+  void wakeup();
+
+private:
+  typedef std::vector<HSQOBJECT> ThreadList;
+  ThreadList threads;
+};
+
+}
+
+#endif
diff --git a/src/scripting/thunderstorm.cpp b/src/scripting/thunderstorm.cpp
new file mode 100644 (file)
index 0000000..26fed3a
--- /dev/null
@@ -0,0 +1,72 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/thunderstorm.hpp"
+#include "scripting/thunderstorm.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  Thunderstorm::Thunderstorm(::Thunderstorm* thunderstorm)
+         : thunderstorm(thunderstorm)
+  {
+  }
+
+  Thunderstorm::~Thunderstorm()
+  {
+  }
+
+  void Thunderstorm::start()
+  {
+    thunderstorm->start();
+  }
+
+  void Thunderstorm::stop()
+  {
+    thunderstorm->stop();
+  }
+
+  void Thunderstorm::thunder()
+  {
+    thunderstorm->thunder();
+  }
+
+  void Thunderstorm::lightning()
+  {
+    thunderstorm->lightning();
+  }
+
+  void Thunderstorm::flash()
+  {
+    thunderstorm->flash();
+  }
+
+  void Thunderstorm::electrify()
+  {
+    thunderstorm->electrify();
+  }
+
+}
diff --git a/src/scripting/thunderstorm.hpp b/src/scripting/thunderstorm.hpp
new file mode 100644 (file)
index 0000000..fbe555c
--- /dev/null
@@ -0,0 +1,76 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_THUNDERSTORM_H__
+#define __SCRIPTING_THUNDERSTORM_H__
+
+#ifndef SCRIPTING_API
+class Thunderstorm;
+typedef Thunderstorm _Thunderstorm;
+#endif
+
+namespace Scripting
+{
+
+class Thunderstorm
+{
+public:
+#ifndef SCRIPTING_API
+  Thunderstorm(_Thunderstorm* thunderstorm);
+  ~Thunderstorm();
+#endif
+
+    /**
+     * Start playing thunder and lightning at configured interval
+     */
+    void start();
+
+    /**
+     * Stop playing thunder and lightning at configured interval
+     */
+    void stop();
+
+    /**
+     * Play thunder
+     */
+    void thunder();
+
+    /**
+     * Play lightning, i.e. call flash() and electrify()
+     */
+    void lightning();
+
+    /**
+     * Display a nice flash
+     */
+    void flash();
+
+    /**
+     * Electrify water throughout the whole sector for a short time
+     */
+    void electrify();
+
+#ifndef SCRIPTING_API
+  _Thunderstorm* thunderstorm;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/tilemap.cpp b/src/scripting/tilemap.cpp
new file mode 100644 (file)
index 0000000..6edf86a
--- /dev/null
@@ -0,0 +1,70 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/tilemap.hpp"
+#include "scripting/tilemap.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  TileMap::TileMap(::TileMap* tilemap)
+    : tilemap(tilemap)
+  { }
+
+  TileMap::~TileMap()
+  { }
+
+  void TileMap::goto_node(int node_no)
+  {
+    tilemap->goto_node(node_no);
+  }
+
+  void TileMap::start_moving()
+  {
+    tilemap->start_moving();
+  }
+
+  void TileMap::stop_moving()
+  {
+    tilemap->stop_moving();
+  }
+
+  void TileMap::fade(float alpha, float seconds)
+  {
+    tilemap->fade(alpha, seconds);
+  }
+
+  void TileMap::set_alpha(float alpha)
+  {
+    tilemap->set_alpha(alpha);
+  }
+
+  float TileMap::get_alpha()
+  {
+    return tilemap->get_alpha();
+  }
+
+}
diff --git a/src/scripting/tilemap.hpp b/src/scripting/tilemap.hpp
new file mode 100644 (file)
index 0000000..331580e
--- /dev/null
@@ -0,0 +1,71 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_TILEMAP_H__
+#define __SCRIPTING_TILEMAP_H__
+
+#ifndef SCRIPTING_API
+class TileMap;
+typedef TileMap _TileMap;
+#endif
+
+namespace Scripting
+{
+
+class TileMap
+{
+public:
+#ifndef SCRIPTING_API
+  TileMap(_TileMap* tilemap);
+  ~TileMap();
+#endif
+
+  /** Move tilemap until at given node, then stop */
+  void goto_node(int node_no);
+
+  /** Start moving tilemap */
+  void start_moving();
+
+  /** Stop tilemap at next node */
+  void stop_moving();
+
+  /**
+   * Start fading the tilemap to opacity given by @c alpha.
+   * Destination opacity will be reached after @c seconds seconds. Also influences solidity.
+   */
+  void fade(float alpha, float seconds);
+
+  /**
+   * Instantly switch tilemap's opacity to @c alpha. Also influences solidity.
+   */
+  void set_alpha(float alpha);
+
+  /**
+   * Return tilemap's opacity. Note that while the tilemap is fading in or out, this will return the current alpha value, not the target alpha.
+   */
+  float get_alpha();
+
+#ifndef SCRIPTING_API
+  _TileMap* tilemap;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/time_scheduler.cpp b/src/scripting/time_scheduler.cpp
new file mode 100644 (file)
index 0000000..d28308b
--- /dev/null
@@ -0,0 +1,100 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <algorithm>
+
+#include "time_scheduler.hpp"
+#include "squirrel_util.hpp"
+#include "squirrel_error.hpp"
+#include "log.hpp"
+
+namespace Scripting
+{
+
+TimeScheduler* TimeScheduler::instance = NULL;
+
+TimeScheduler::TimeScheduler()
+{
+}
+
+TimeScheduler::~TimeScheduler()
+{
+}
+
+void
+TimeScheduler::update(float time)
+{
+  while(!schedule.empty() && schedule.front().wakeup_time < time) {
+    HSQOBJECT thread_ref = schedule.front().thread_ref;
+
+    sq_pushobject(global_vm, thread_ref);
+    sq_getweakrefval(global_vm, -1);
+
+    HSQUIRRELVM scheduled_vm;
+    if(sq_gettype(global_vm, -1) == OT_THREAD &&
+        SQ_SUCCEEDED(sq_getthread(global_vm, -1, &scheduled_vm))) {
+      if(SQ_FAILED(sq_wakeupvm(scheduled_vm, SQFalse, SQFalse, SQTrue))) {
+        std::ostringstream msg;
+        msg << "Couldn't wakeup scheduled squirrel VM: ";
+        sq_getlasterror(scheduled_vm);
+        if(sq_gettype(scheduled_vm, -1) != OT_STRING) {
+            msg << "(no info)";
+        } else {
+            const char* lasterr;
+            sq_getstring(scheduled_vm, -1, &lasterr);
+            msg << lasterr;
+        }
+        log_warning << msg.str() << std::endl;
+        sq_pop(scheduled_vm, 1);
+      }
+    }
+
+    sq_release(global_vm, &thread_ref);
+    sq_pop(global_vm, 2);
+
+    std::pop_heap(schedule.begin(), schedule.end());
+    schedule.pop_back();
+  }
+}
+
+void
+TimeScheduler::schedule_thread(HSQUIRRELVM scheduled_vm, float time)
+{
+  // create a weakref to the VM
+  SQObject vm_obj = vm_to_object(scheduled_vm);
+  sq_pushobject(global_vm, vm_obj);
+  sq_weakref(global_vm, -1);
+
+  ScheduleEntry entry;
+  if(SQ_FAILED(sq_getstackobj(global_vm, -1, & entry.thread_ref))) {
+    sq_pop(global_vm, 2);
+    throw SquirrelError(global_vm, "Couldn't get thread weakref from vm");
+  }
+  entry.wakeup_time = time;
+
+  sq_addref(global_vm, & entry.thread_ref);
+  sq_pop(global_vm, 2);
+
+  schedule.push_back(entry);
+  std::push_heap(schedule.begin(), schedule.end());
+}
+
+}
diff --git a/src/scripting/time_scheduler.hpp b/src/scripting/time_scheduler.hpp
new file mode 100644 (file)
index 0000000..d26cb76
--- /dev/null
@@ -0,0 +1,64 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __TIME_SCHEDULER_HPP__
+#define __TIME_SCHEDULER_HPP__
+
+#include <vector>
+#include <squirrel.h>
+
+namespace Scripting
+{
+
+/**
+ * This class keeps a list of squirrel threads that are scheduled for a certain
+ * time. (the typical result of a wait() command in a squirrel script)
+ */
+class TimeScheduler
+{
+public:
+  TimeScheduler();
+  virtual ~TimeScheduler();
+
+  void update(float time);
+  void schedule_thread(HSQUIRRELVM vm, float time);
+
+  static TimeScheduler* instance;
+
+private:
+  struct ScheduleEntry {
+    /// weak reference to the squirrel vm object
+    HSQOBJECT thread_ref;
+    /// time when the thread should be woken up
+    float wakeup_time;
+
+    bool operator<(const ScheduleEntry& other) const
+    {
+      // we need the smallest value on top
+      return wakeup_time > other.wakeup_time;
+    }
+  };
+
+  typedef std::vector<ScheduleEntry> ScheduleHeap;
+  ScheduleHeap schedule;
+};
+
+}
+
+#endif
diff --git a/src/scripting/willowisp.hpp b/src/scripting/willowisp.hpp
new file mode 100644 (file)
index 0000000..33049fa
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_WILLOWISP_H__
+#define __SCRIPTING_WILLOWISP_H__
+
+namespace Scripting
+{
+
+class WillOWisp
+{
+public:
+#ifndef SCRIPTING_API
+  virtual ~WillOWisp()
+  {}
+#endif
+
+  /** Move willowisp to given node */
+  virtual void goto_node(int node_no) = 0;
+
+  /** set willowisp state can be:
+   * -stopped          willowisp doesn't move
+   * -move_path        willowisp moves along the path (call goto_node)
+   * -move_path_track  willowisp moves along path but catchs tux when he is near
+   * -normal           "normal" mode starts tracking tux when he is near enough
+   * -vanish           vanish
+   */
+  virtual void set_state(const std::string& state) = 0;
+
+  virtual void start_moving() = 0;
+  virtual void stop_moving() = 0;
+};
+
+}
+
+#endif
diff --git a/src/scripting/wind.cpp b/src/scripting/wind.cpp
new file mode 100644 (file)
index 0000000..a90a520
--- /dev/null
@@ -0,0 +1,50 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/wind.hpp"
+#include "scripting/wind.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL      log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+  Wind::Wind(::Wind* wind)
+    : wind(wind)
+  { }
+
+  Wind::~Wind()
+  { }
+
+  void Wind::start()
+  {
+    wind->start();
+  }
+
+  void Wind::stop()
+  {
+    wind->stop();
+  }
+
+}
diff --git a/src/scripting/wind.hpp b/src/scripting/wind.hpp
new file mode 100644 (file)
index 0000000..72d541a
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTING_WIND_H__
+#define __SCRIPTING_WIND_H__
+
+#ifndef SCRIPTING_API
+class Wind;
+typedef Wind _Wind;
+#endif
+
+namespace Scripting
+{
+
+class Wind
+{
+public:
+#ifndef SCRIPTING_API
+  Wind(_Wind* wind);
+  ~Wind();
+#endif
+
+  /** Start wind */
+  void start();
+
+  /** Stop wind */
+  void stop();
+
+#ifndef SCRIPTING_API
+  _Wind* wind;
+#endif
+};
+
+}
+
+#endif
diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp
new file mode 100644 (file)
index 0000000..29e6197
--- /dev/null
@@ -0,0 +1,5106 @@
+/**
+ * WARNING: This file is automatically generated from:
+ *  'src/scripting/wrapper.interface.hpp'
+ * DO NOT CHANGE
+ */
+#include <config.h>
+
+#include <new>
+#include <assert.h>
+#include <string>
+#include <sstream>
+#include <squirrel.h>
+#include "squirrel_error.hpp"
+#include "wrapper.interface.hpp"
+
+namespace Scripting
+{
+namespace Wrapper
+{
+
+static SQInteger DisplayEffect_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger DisplayEffect_fade_out_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_out' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_out(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger DisplayEffect_fade_in_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_in' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_in(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger DisplayEffect_set_black_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_black' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_black(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_black'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger DisplayEffect_is_black_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'is_black' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+
+  try {
+    bool return_value = _this->is_black();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_black'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger DisplayEffect_sixteen_to_nine_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'sixteen_to_nine' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->sixteen_to_nine(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'sixteen_to_nine'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger DisplayEffect_four_to_three_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'four_to_three' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->four_to_three(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'four_to_three'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Camera_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Camera_reload_config_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'reload_config' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+
+  try {
+    _this->reload_config();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'reload_config'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Camera_shake_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'shake' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg2;
+  if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+    sq_throwerror(vm, _SC("Argument 3 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->shake(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'shake'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Camera_set_pos_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_pos' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Camera_set_mode_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_mode' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_mode(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_mode'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Camera_scroll_to_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'scroll_to' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg2;
+  if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+    sq_throwerror(vm, _SC("Argument 3 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->scroll_to(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'scroll_to'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Level_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Level_finish_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'finish' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->finish(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'finish'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Level_spawn_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'spawn' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+  const SQChar* arg1;
+  if(SQ_FAILED(sq_getstring(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->spawn(arg0, arg1);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'spawn'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Level_flip_vertically_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'flip_vertically' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+
+  try {
+    _this->flip_vertically();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'flip_vertically'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Level_toggle_pause_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'toggle_pause' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+
+  try {
+    _this->toggle_pause();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'toggle_pause'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger ScriptedObject_set_action_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_action' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_action(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_action'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_action_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_action' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    std::string return_value = _this->get_action();
+
+    sq_pushstring(vm, return_value.c_str(), return_value.size());
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_action'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_move_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'move' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->move(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'move'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_set_pos_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_pos' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    float return_value = _this->get_pos_x();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    float return_value = _this->get_pos_y();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_set_velocity_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_velocity' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_velocity(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_velocity'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_velocity_x_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_velocity_x' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    float return_value = _this->get_velocity_x();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_velocity_x'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_velocity_y_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_velocity_y' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    float return_value = _this->get_velocity_y();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_velocity_y'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_set_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_visible(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_is_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'is_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    bool return_value = _this->is_visible();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_set_solid_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_solid' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_solid(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_solid'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_is_solid_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'is_solid' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    bool return_value = _this->is_solid();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_solid'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ScriptedObject_get_name_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_name' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+  try {
+    std::string return_value = _this->get_name();
+
+    sq_pushstring(vm, return_value.c_str(), return_value.size());
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_name'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Text_set_text_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_text' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_text(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_text'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_set_font_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_font' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_font(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_font'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_fade_in_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_in' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_in(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_fade_out_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_out' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_out(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_set_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_visible(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_set_centered_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_centered' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_centered(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_centered'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_set_pos_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_pos' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+  try {
+    float return_value = _this->get_pos_x();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+  try {
+    float return_value = _this->get_pos_y();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_set_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_anchor_point' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_anchor_point(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_anchor_point'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Text_get_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_anchor_point' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+  try {
+    int return_value = _this->get_anchor_point();
+
+    sq_pushinteger(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_anchor_point'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Player_add_bonus_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'add_bonus' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    bool return_value = _this->add_bonus(arg0);
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'add_bonus'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_add_coins_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'add_coins' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->add_coins(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'add_coins'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_make_invincible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'make_invincible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->make_invincible();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'make_invincible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_deactivate_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'deactivate' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->deactivate();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'deactivate'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_activate_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'activate' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->activate();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'activate'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_walk_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'walk' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->walk(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'walk'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_set_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_visible(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_get_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    bool return_value = _this->get_visible();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_kill_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'kill' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->kill(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'kill'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_set_ghost_mode_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_ghost_mode' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_ghost_mode(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_ghost_mode'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_get_ghost_mode_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_ghost_mode' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    bool return_value = _this->get_ghost_mode();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ghost_mode'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_do_cheer_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'do_cheer' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->do_cheer();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_cheer'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_do_duck_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'do_duck' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->do_duck();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_duck'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_do_standup_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'do_standup' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->do_standup();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_standup'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_do_backflip_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'do_backflip' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+  try {
+    _this->do_backflip();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_backflip'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_do_jump_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'do_jump' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->do_jump(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_jump'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Player_trigger_sequence_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'trigger_sequence' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->trigger_sequence(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'trigger_sequence'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger FloatingImage_constructor_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::FloatingImage* _this = new Scripting::FloatingImage(arg0);
+  if(SQ_FAILED(sq_setinstanceup(vm, 1, _this))) {
+    sq_throwerror(vm, _SC("Couldn't setup instance of 'FloatingImage' class"));
+    return SQ_ERROR;
+  }
+  sq_setreleasehook(vm, 1, FloatingImage_release_hook);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'constructor'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_set_layer_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_layer' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_layer(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_layer'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_layer_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_layer' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    int return_value = _this->get_layer();
+
+    sq_pushinteger(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_layer'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_set_pos_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_pos' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    float return_value = _this->get_pos_x();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    float return_value = _this->get_pos_y();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_set_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_anchor_point' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_anchor_point(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_anchor_point'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_anchor_point' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    int return_value = _this->get_anchor_point();
+
+    sq_pushinteger(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_anchor_point'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_set_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_visible(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_visible_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_visible' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    bool return_value = _this->get_visible();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_visible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_set_action_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_action' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_action(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_action'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_get_action_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_action' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+  try {
+    std::string return_value = _this->get_action();
+
+    sq_pushstring(vm, return_value.c_str(), return_value.size());
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_action'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_fade_in_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_in' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_in(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger FloatingImage_fade_out_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade_out' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade_out(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Platform_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Platform_goto_node_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'goto_node' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->goto_node(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Platform_start_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+
+  try {
+    _this->start_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Platform_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+
+  try {
+    _this->stop_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Candle_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Candle_get_burning_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_burning' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (data);
+
+  try {
+    bool return_value = _this->get_burning();
+
+    sq_pushbool(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_burning'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Candle_set_burning_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_burning' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (data);
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_burning(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_burning'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Wind_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Wind_start_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (data);
+
+  try {
+    _this->start();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Wind_stop_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (data);
+
+  try {
+    _this->stop();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger AmbientSound_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger AmbientSound_set_pos_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_pos' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger AmbientSound_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+
+  try {
+    float return_value = _this->get_pos_x();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger AmbientSound_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+
+  try {
+    float return_value = _this->get_pos_y();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger Thunderstorm_start_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->start();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_stop_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->stop();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_thunder_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'thunder' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->thunder();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'thunder'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_lightning_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'lightning' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->lightning();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'lightning'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_flash_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'flash' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->flash();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'flash'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger Thunderstorm_electrify_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'electrify' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+  try {
+    _this->electrify();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'electrify'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger TileMap_goto_node_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'goto_node' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->goto_node(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_start_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+  try {
+    _this->start_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+  try {
+    _this->stop_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_fade_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'fade' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->fade(static_cast<float> (arg0), static_cast<float> (arg1));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_set_alpha_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_alpha' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_alpha(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_alpha'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger TileMap_get_alpha_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_alpha' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+  try {
+    float return_value = _this->get_alpha();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_alpha'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger SSector_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger SSector_set_ambient_light_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_ambient_light' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg2;
+  if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+    sq_throwerror(vm, _SC("Argument 3 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_ambient_light(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_ambient_light'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger SSector_get_ambient_red_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_ambient_red' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+  try {
+    float return_value = _this->get_ambient_red();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_red'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger SSector_get_ambient_green_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_ambient_green' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+  try {
+    float return_value = _this->get_ambient_green();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_green'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger SSector_get_ambient_blue_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_ambient_blue' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+  try {
+    float return_value = _this->get_ambient_blue();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_blue'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger LevelTime_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger LevelTime_start_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+  try {
+    _this->start();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger LevelTime_stop_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+  try {
+    _this->stop();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger LevelTime_get_time_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'get_time' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+  try {
+    float return_value = _this->get_time();
+
+    sq_pushfloat(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_time'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger LevelTime_set_time_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_time' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_time(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_time'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger WillOWisp_release_hook(SQUserPointer ptr, SQInteger )
+{
+  Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (ptr);
+  delete _this;
+  return 0;
+}
+
+static SQInteger WillOWisp_goto_node_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'goto_node' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+  SQInteger arg0;
+  if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not an integer"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->goto_node(static_cast<int> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger WillOWisp_set_state_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'set_state' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    _this->set_state(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_state'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger WillOWisp_start_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'start_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+
+  try {
+    _this->start_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger WillOWisp_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+  SQUserPointer data;
+  if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+    sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+    return SQ_ERROR;
+  }
+  Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+
+  try {
+    _this->stop_moving();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger display_wrapper(HSQUIRRELVM vm)
+{
+  return Scripting::display(vm);
+}
+
+static SQInteger print_stacktrace_wrapper(HSQUIRRELVM vm)
+{
+  HSQUIRRELVM arg0 = vm;
+
+  try {
+    Scripting::print_stacktrace(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'print_stacktrace'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger get_current_thread_wrapper(HSQUIRRELVM vm)
+{
+  return Scripting::get_current_thread(vm);
+}
+
+static SQInteger display_text_file_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::display_text_file(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'display_text_file'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger load_worldmap_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::load_worldmap(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'load_worldmap'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger load_level_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::load_level(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'load_level'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger wait_wrapper(HSQUIRRELVM vm)
+{
+  HSQUIRRELVM arg0 = vm;
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::wait(arg0, static_cast<float> (arg1));
+
+    return sq_suspendvm(vm);
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'wait'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger wait_for_screenswitch_wrapper(HSQUIRRELVM vm)
+{
+  HSQUIRRELVM arg0 = vm;
+
+  try {
+    Scripting::wait_for_screenswitch(arg0);
+
+    return sq_suspendvm(vm);
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'wait_for_screenswitch'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger exit_screen_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::exit_screen();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'exit_screen'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger fadeout_screen_wrapper(HSQUIRRELVM vm)
+{
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::fadeout_screen(static_cast<float> (arg0));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'fadeout_screen'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger shrink_screen_wrapper(HSQUIRRELVM vm)
+{
+  SQFloat arg0;
+  if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg1;
+  if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 2 not a float"));
+    return SQ_ERROR;
+  }
+  SQFloat arg2;
+  if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+    sq_throwerror(vm, _SC("Argument 3 not a float"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::shrink_screen(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'shrink_screen'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger abort_screenfade_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::abort_screenfade();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'abort_screenfade'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger translate_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    std::string return_value = Scripting::translate(arg0);
+
+    sq_pushstring(vm, return_value.c_str(), return_value.size());
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'translate'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger import_wrapper(HSQUIRRELVM vm)
+{
+  HSQUIRRELVM arg0 = vm;
+  const SQChar* arg1;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg1))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::import(arg0, arg1);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'import'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger save_state_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::save_state();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'save_state'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger update_worldmap_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::update_worldmap();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'update_worldmap'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger debug_collrects_wrapper(HSQUIRRELVM vm)
+{
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::debug_collrects(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_collrects'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger debug_show_fps_wrapper(HSQUIRRELVM vm)
+{
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::debug_show_fps(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_show_fps'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger debug_draw_solids_only_wrapper(HSQUIRRELVM vm)
+{
+  SQBool arg0;
+  if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a bool"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::debug_draw_solids_only(arg0 == SQTrue);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_draw_solids_only'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger play_music_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::play_music(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'play_music'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger play_sound_wrapper(HSQUIRRELVM vm)
+{
+  const SQChar* arg0;
+  if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+    sq_throwerror(vm, _SC("Argument 1 not a string"));
+    return SQ_ERROR;
+  }
+
+  try {
+    Scripting::play_sound(arg0);
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'play_sound'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger grease_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::grease();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'grease'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger invincible_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::invincible();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'invincible'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger ghost_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::ghost();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'ghost'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger mortal_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::mortal();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'mortal'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger restart_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::restart();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'restart'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger whereami_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::whereami();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'whereami'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger gotoend_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::gotoend();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'gotoend'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger camera_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::camera();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'camera'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger quit_wrapper(HSQUIRRELVM vm)
+{
+  (void) vm;
+
+  try {
+    Scripting::quit();
+
+    return 0;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'quit'"));
+    return SQ_ERROR;
+  }
+
+}
+
+static SQInteger rand_wrapper(HSQUIRRELVM vm)
+{
+
+  try {
+    int return_value = Scripting::rand();
+
+    sq_pushinteger(vm, return_value);
+    return 1;
+
+  } catch(std::exception& e) {
+    sq_throwerror(vm, e.what());
+    return SQ_ERROR;
+  } catch(...) {
+    sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand'"));
+    return SQ_ERROR;
+  }
+
+}
+
+} // end of namespace Wrapper
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::DisplayEffect* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "DisplayEffect", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'DisplayEffect'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'DisplayEffect'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, DisplayEffect_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Camera* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Camera", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Camera'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Camera'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Camera_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Level* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Level", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Level'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Level'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Level_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::ScriptedObject* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "ScriptedObject", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'ScriptedObject'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'ScriptedObject'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, ScriptedObject_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Text* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Text", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Text'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Text'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Text_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Player* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Player", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Player'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Player'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Player_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "FloatingImage", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'FloatingImage'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'FloatingImage'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, FloatingImage_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Platform* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Platform", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Platform'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Platform'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Platform_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Candle* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Candle", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Candle'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Candle'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Candle_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Wind* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Wind", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Wind'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Wind'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Wind_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::AmbientSound* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "AmbientSound", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'AmbientSound'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'AmbientSound'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, AmbientSound_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Thunderstorm* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "Thunderstorm", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'Thunderstorm'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'Thunderstorm'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, Thunderstorm_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::TileMap* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "TileMap", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'TileMap'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'TileMap'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, TileMap_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::SSector* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "SSector", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'SSector'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'SSector'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, SSector_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::LevelTime* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "LevelTime", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'LevelTime'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'LevelTime'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, LevelTime_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::WillOWisp* object, bool setup_releasehook)
+{
+  using namespace Wrapper;
+
+  sq_pushroottable(v);
+  sq_pushstring(v, "WillOWisp", -1);
+  if(SQ_FAILED(sq_get(v, -2))) {
+    std::ostringstream msg;
+    msg << "Couldn't resolved squirrel type 'WillOWisp'";
+    throw SquirrelError(v, msg.str());
+  }
+
+  if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+    std::ostringstream msg;
+    msg << "Couldn't setup squirrel instance for object of type 'WillOWisp'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_remove(v, -2); // remove object name
+
+  if(setup_releasehook) {
+    sq_setreleasehook(v, -1, WillOWisp_release_hook);
+  }
+
+  sq_remove(v, -2); // remove root table
+}
+
+void register_supertux_wrapper(HSQUIRRELVM v)
+{
+  using namespace Wrapper;
+
+  sq_pushstring(v, "ANCHOR_TOP", -1);
+  sq_pushinteger(v, 16);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP'");
+  }
+
+  sq_pushstring(v, "ANCHOR_BOTTOM", -1);
+  sq_pushinteger(v, 32);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM'");
+  }
+
+  sq_pushstring(v, "ANCHOR_LEFT", -1);
+  sq_pushinteger(v, 1);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_LEFT'");
+  }
+
+  sq_pushstring(v, "ANCHOR_RIGHT", -1);
+  sq_pushinteger(v, 2);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_RIGHT'");
+  }
+
+  sq_pushstring(v, "ANCHOR_MIDDLE", -1);
+  sq_pushinteger(v, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_MIDDLE'");
+  }
+
+  sq_pushstring(v, "ANCHOR_TOP_LEFT", -1);
+  sq_pushinteger(v, 17);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP_LEFT'");
+  }
+
+  sq_pushstring(v, "ANCHOR_TOP_RIGHT", -1);
+  sq_pushinteger(v, 18);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP_RIGHT'");
+  }
+
+  sq_pushstring(v, "ANCHOR_BOTTOM_LEFT", -1);
+  sq_pushinteger(v, 33);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM_LEFT'");
+  }
+
+  sq_pushstring(v, "ANCHOR_BOTTOM_RIGHT", -1);
+  sq_pushinteger(v, 34);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM_RIGHT'");
+  }
+
+  sq_pushstring(v, "display", -1);
+  sq_newclosure(v, &display_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'display'");
+  }
+
+  sq_pushstring(v, "print_stacktrace", -1);
+  sq_newclosure(v, &print_stacktrace_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'print_stacktrace'");
+  }
+
+  sq_pushstring(v, "get_current_thread", -1);
+  sq_newclosure(v, &get_current_thread_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_current_thread'");
+  }
+
+  sq_pushstring(v, "display_text_file", -1);
+  sq_newclosure(v, &display_text_file_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'display_text_file'");
+  }
+
+  sq_pushstring(v, "load_worldmap", -1);
+  sq_newclosure(v, &load_worldmap_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'load_worldmap'");
+  }
+
+  sq_pushstring(v, "load_level", -1);
+  sq_newclosure(v, &load_level_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'load_level'");
+  }
+
+  sq_pushstring(v, "wait", -1);
+  sq_newclosure(v, &wait_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'wait'");
+  }
+
+  sq_pushstring(v, "wait_for_screenswitch", -1);
+  sq_newclosure(v, &wait_for_screenswitch_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'wait_for_screenswitch'");
+  }
+
+  sq_pushstring(v, "exit_screen", -1);
+  sq_newclosure(v, &exit_screen_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'exit_screen'");
+  }
+
+  sq_pushstring(v, "fadeout_screen", -1);
+  sq_newclosure(v, &fadeout_screen_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fadeout_screen'");
+  }
+
+  sq_pushstring(v, "shrink_screen", -1);
+  sq_newclosure(v, &shrink_screen_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'shrink_screen'");
+  }
+
+  sq_pushstring(v, "abort_screenfade", -1);
+  sq_newclosure(v, &abort_screenfade_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'abort_screenfade'");
+  }
+
+  sq_pushstring(v, "translate", -1);
+  sq_newclosure(v, &translate_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'translate'");
+  }
+
+  sq_pushstring(v, "import", -1);
+  sq_newclosure(v, &import_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'import'");
+  }
+
+  sq_pushstring(v, "save_state", -1);
+  sq_newclosure(v, &save_state_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'save_state'");
+  }
+
+  sq_pushstring(v, "update_worldmap", -1);
+  sq_newclosure(v, &update_worldmap_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'update_worldmap'");
+  }
+
+  sq_pushstring(v, "debug_collrects", -1);
+  sq_newclosure(v, &debug_collrects_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'debug_collrects'");
+  }
+
+  sq_pushstring(v, "debug_show_fps", -1);
+  sq_newclosure(v, &debug_show_fps_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'debug_show_fps'");
+  }
+
+  sq_pushstring(v, "debug_draw_solids_only", -1);
+  sq_newclosure(v, &debug_draw_solids_only_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'debug_draw_solids_only'");
+  }
+
+  sq_pushstring(v, "play_music", -1);
+  sq_newclosure(v, &play_music_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'play_music'");
+  }
+
+  sq_pushstring(v, "play_sound", -1);
+  sq_newclosure(v, &play_sound_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'play_sound'");
+  }
+
+  sq_pushstring(v, "grease", -1);
+  sq_newclosure(v, &grease_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'grease'");
+  }
+
+  sq_pushstring(v, "invincible", -1);
+  sq_newclosure(v, &invincible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'invincible'");
+  }
+
+  sq_pushstring(v, "ghost", -1);
+  sq_newclosure(v, &ghost_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'ghost'");
+  }
+
+  sq_pushstring(v, "mortal", -1);
+  sq_newclosure(v, &mortal_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'mortal'");
+  }
+
+  sq_pushstring(v, "restart", -1);
+  sq_newclosure(v, &restart_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'restart'");
+  }
+
+  sq_pushstring(v, "whereami", -1);
+  sq_newclosure(v, &whereami_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'whereami'");
+  }
+
+  sq_pushstring(v, "gotoend", -1);
+  sq_newclosure(v, &gotoend_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'gotoend'");
+  }
+
+  sq_pushstring(v, "camera", -1);
+  sq_newclosure(v, &camera_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'camera'");
+  }
+
+  sq_pushstring(v, "quit", -1);
+  sq_newclosure(v, &quit_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'quit'");
+  }
+
+  sq_pushstring(v, "rand", -1);
+  sq_newclosure(v, &rand_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'rand'");
+  }
+
+  // Register class DisplayEffect
+  sq_pushstring(v, "DisplayEffect", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'DisplayEffect'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "fade_out", -1);
+  sq_newclosure(v, &DisplayEffect_fade_out_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_out'");
+  }
+
+  sq_pushstring(v, "fade_in", -1);
+  sq_newclosure(v, &DisplayEffect_fade_in_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_in'");
+  }
+
+  sq_pushstring(v, "set_black", -1);
+  sq_newclosure(v, &DisplayEffect_set_black_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_black'");
+  }
+
+  sq_pushstring(v, "is_black", -1);
+  sq_newclosure(v, &DisplayEffect_is_black_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'is_black'");
+  }
+
+  sq_pushstring(v, "sixteen_to_nine", -1);
+  sq_newclosure(v, &DisplayEffect_sixteen_to_nine_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'sixteen_to_nine'");
+  }
+
+  sq_pushstring(v, "four_to_three", -1);
+  sq_newclosure(v, &DisplayEffect_four_to_three_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'four_to_three'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'DisplayEffect'");
+  }
+
+  // Register class Camera
+  sq_pushstring(v, "Camera", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Camera'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "reload_config", -1);
+  sq_newclosure(v, &Camera_reload_config_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'reload_config'");
+  }
+
+  sq_pushstring(v, "shake", -1);
+  sq_newclosure(v, &Camera_shake_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'shake'");
+  }
+
+  sq_pushstring(v, "set_pos", -1);
+  sq_newclosure(v, &Camera_set_pos_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_pos'");
+  }
+
+  sq_pushstring(v, "set_mode", -1);
+  sq_newclosure(v, &Camera_set_mode_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_mode'");
+  }
+
+  sq_pushstring(v, "scroll_to", -1);
+  sq_newclosure(v, &Camera_scroll_to_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'scroll_to'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Camera'");
+  }
+
+  // Register class Level
+  sq_pushstring(v, "Level", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Level'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "finish", -1);
+  sq_newclosure(v, &Level_finish_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'finish'");
+  }
+
+  sq_pushstring(v, "spawn", -1);
+  sq_newclosure(v, &Level_spawn_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'spawn'");
+  }
+
+  sq_pushstring(v, "flip_vertically", -1);
+  sq_newclosure(v, &Level_flip_vertically_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'flip_vertically'");
+  }
+
+  sq_pushstring(v, "toggle_pause", -1);
+  sq_newclosure(v, &Level_toggle_pause_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'toggle_pause'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Level'");
+  }
+
+  // Register class ScriptedObject
+  sq_pushstring(v, "ScriptedObject", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'ScriptedObject'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "set_action", -1);
+  sq_newclosure(v, &ScriptedObject_set_action_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_action'");
+  }
+
+  sq_pushstring(v, "get_action", -1);
+  sq_newclosure(v, &ScriptedObject_get_action_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_action'");
+  }
+
+  sq_pushstring(v, "move", -1);
+  sq_newclosure(v, &ScriptedObject_move_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'move'");
+  }
+
+  sq_pushstring(v, "set_pos", -1);
+  sq_newclosure(v, &ScriptedObject_set_pos_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_pos'");
+  }
+
+  sq_pushstring(v, "get_pos_x", -1);
+  sq_newclosure(v, &ScriptedObject_get_pos_x_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+  }
+
+  sq_pushstring(v, "get_pos_y", -1);
+  sq_newclosure(v, &ScriptedObject_get_pos_y_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+  }
+
+  sq_pushstring(v, "set_velocity", -1);
+  sq_newclosure(v, &ScriptedObject_set_velocity_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_velocity'");
+  }
+
+  sq_pushstring(v, "get_velocity_x", -1);
+  sq_newclosure(v, &ScriptedObject_get_velocity_x_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_velocity_x'");
+  }
+
+  sq_pushstring(v, "get_velocity_y", -1);
+  sq_newclosure(v, &ScriptedObject_get_velocity_y_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_velocity_y'");
+  }
+
+  sq_pushstring(v, "set_visible", -1);
+  sq_newclosure(v, &ScriptedObject_set_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_visible'");
+  }
+
+  sq_pushstring(v, "is_visible", -1);
+  sq_newclosure(v, &ScriptedObject_is_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'is_visible'");
+  }
+
+  sq_pushstring(v, "set_solid", -1);
+  sq_newclosure(v, &ScriptedObject_set_solid_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_solid'");
+  }
+
+  sq_pushstring(v, "is_solid", -1);
+  sq_newclosure(v, &ScriptedObject_is_solid_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'is_solid'");
+  }
+
+  sq_pushstring(v, "get_name", -1);
+  sq_newclosure(v, &ScriptedObject_get_name_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_name'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'ScriptedObject'");
+  }
+
+  // Register class Text
+  sq_pushstring(v, "Text", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Text'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "set_text", -1);
+  sq_newclosure(v, &Text_set_text_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_text'");
+  }
+
+  sq_pushstring(v, "set_font", -1);
+  sq_newclosure(v, &Text_set_font_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_font'");
+  }
+
+  sq_pushstring(v, "fade_in", -1);
+  sq_newclosure(v, &Text_fade_in_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_in'");
+  }
+
+  sq_pushstring(v, "fade_out", -1);
+  sq_newclosure(v, &Text_fade_out_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_out'");
+  }
+
+  sq_pushstring(v, "set_visible", -1);
+  sq_newclosure(v, &Text_set_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_visible'");
+  }
+
+  sq_pushstring(v, "set_centered", -1);
+  sq_newclosure(v, &Text_set_centered_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_centered'");
+  }
+
+  sq_pushstring(v, "set_pos", -1);
+  sq_newclosure(v, &Text_set_pos_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_pos'");
+  }
+
+  sq_pushstring(v, "get_pos_x", -1);
+  sq_newclosure(v, &Text_get_pos_x_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+  }
+
+  sq_pushstring(v, "get_pos_y", -1);
+  sq_newclosure(v, &Text_get_pos_y_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+  }
+
+  sq_pushstring(v, "set_anchor_point", -1);
+  sq_newclosure(v, &Text_set_anchor_point_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_anchor_point'");
+  }
+
+  sq_pushstring(v, "get_anchor_point", -1);
+  sq_newclosure(v, &Text_get_anchor_point_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_anchor_point'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Text'");
+  }
+
+  // Register class Player
+  sq_pushstring(v, "Player", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Player'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "add_bonus", -1);
+  sq_newclosure(v, &Player_add_bonus_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'add_bonus'");
+  }
+
+  sq_pushstring(v, "add_coins", -1);
+  sq_newclosure(v, &Player_add_coins_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'add_coins'");
+  }
+
+  sq_pushstring(v, "make_invincible", -1);
+  sq_newclosure(v, &Player_make_invincible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'make_invincible'");
+  }
+
+  sq_pushstring(v, "deactivate", -1);
+  sq_newclosure(v, &Player_deactivate_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'deactivate'");
+  }
+
+  sq_pushstring(v, "activate", -1);
+  sq_newclosure(v, &Player_activate_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'activate'");
+  }
+
+  sq_pushstring(v, "walk", -1);
+  sq_newclosure(v, &Player_walk_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'walk'");
+  }
+
+  sq_pushstring(v, "set_visible", -1);
+  sq_newclosure(v, &Player_set_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_visible'");
+  }
+
+  sq_pushstring(v, "get_visible", -1);
+  sq_newclosure(v, &Player_get_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_visible'");
+  }
+
+  sq_pushstring(v, "kill", -1);
+  sq_newclosure(v, &Player_kill_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'kill'");
+  }
+
+  sq_pushstring(v, "set_ghost_mode", -1);
+  sq_newclosure(v, &Player_set_ghost_mode_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_ghost_mode'");
+  }
+
+  sq_pushstring(v, "get_ghost_mode", -1);
+  sq_newclosure(v, &Player_get_ghost_mode_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_ghost_mode'");
+  }
+
+  sq_pushstring(v, "do_cheer", -1);
+  sq_newclosure(v, &Player_do_cheer_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'do_cheer'");
+  }
+
+  sq_pushstring(v, "do_duck", -1);
+  sq_newclosure(v, &Player_do_duck_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'do_duck'");
+  }
+
+  sq_pushstring(v, "do_standup", -1);
+  sq_newclosure(v, &Player_do_standup_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'do_standup'");
+  }
+
+  sq_pushstring(v, "do_backflip", -1);
+  sq_newclosure(v, &Player_do_backflip_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'do_backflip'");
+  }
+
+  sq_pushstring(v, "do_jump", -1);
+  sq_newclosure(v, &Player_do_jump_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'do_jump'");
+  }
+
+  sq_pushstring(v, "trigger_sequence", -1);
+  sq_newclosure(v, &Player_trigger_sequence_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'trigger_sequence'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Player'");
+  }
+
+  // Register class FloatingImage
+  sq_pushstring(v, "FloatingImage", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'FloatingImage'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "constructor", -1);
+  sq_newclosure(v, &FloatingImage_constructor_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'constructor'");
+  }
+
+  sq_pushstring(v, "set_layer", -1);
+  sq_newclosure(v, &FloatingImage_set_layer_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_layer'");
+  }
+
+  sq_pushstring(v, "get_layer", -1);
+  sq_newclosure(v, &FloatingImage_get_layer_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_layer'");
+  }
+
+  sq_pushstring(v, "set_pos", -1);
+  sq_newclosure(v, &FloatingImage_set_pos_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_pos'");
+  }
+
+  sq_pushstring(v, "get_pos_x", -1);
+  sq_newclosure(v, &FloatingImage_get_pos_x_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+  }
+
+  sq_pushstring(v, "get_pos_y", -1);
+  sq_newclosure(v, &FloatingImage_get_pos_y_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+  }
+
+  sq_pushstring(v, "set_anchor_point", -1);
+  sq_newclosure(v, &FloatingImage_set_anchor_point_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_anchor_point'");
+  }
+
+  sq_pushstring(v, "get_anchor_point", -1);
+  sq_newclosure(v, &FloatingImage_get_anchor_point_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_anchor_point'");
+  }
+
+  sq_pushstring(v, "set_visible", -1);
+  sq_newclosure(v, &FloatingImage_set_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_visible'");
+  }
+
+  sq_pushstring(v, "get_visible", -1);
+  sq_newclosure(v, &FloatingImage_get_visible_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_visible'");
+  }
+
+  sq_pushstring(v, "set_action", -1);
+  sq_newclosure(v, &FloatingImage_set_action_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_action'");
+  }
+
+  sq_pushstring(v, "get_action", -1);
+  sq_newclosure(v, &FloatingImage_get_action_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_action'");
+  }
+
+  sq_pushstring(v, "fade_in", -1);
+  sq_newclosure(v, &FloatingImage_fade_in_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_in'");
+  }
+
+  sq_pushstring(v, "fade_out", -1);
+  sq_newclosure(v, &FloatingImage_fade_out_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade_out'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'FloatingImage'");
+  }
+
+  // Register class Platform
+  sq_pushstring(v, "Platform", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Platform'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "goto_node", -1);
+  sq_newclosure(v, &Platform_goto_node_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'goto_node'");
+  }
+
+  sq_pushstring(v, "start_moving", -1);
+  sq_newclosure(v, &Platform_start_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start_moving'");
+  }
+
+  sq_pushstring(v, "stop_moving", -1);
+  sq_newclosure(v, &Platform_stop_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Platform'");
+  }
+
+  // Register class Candle
+  sq_pushstring(v, "Candle", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Candle'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "get_burning", -1);
+  sq_newclosure(v, &Candle_get_burning_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_burning'");
+  }
+
+  sq_pushstring(v, "set_burning", -1);
+  sq_newclosure(v, &Candle_set_burning_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_burning'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Candle'");
+  }
+
+  // Register class Wind
+  sq_pushstring(v, "Wind", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Wind'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "start", -1);
+  sq_newclosure(v, &Wind_start_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start'");
+  }
+
+  sq_pushstring(v, "stop", -1);
+  sq_newclosure(v, &Wind_stop_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Wind'");
+  }
+
+  // Register class AmbientSound
+  sq_pushstring(v, "AmbientSound", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'AmbientSound'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "set_pos", -1);
+  sq_newclosure(v, &AmbientSound_set_pos_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_pos'");
+  }
+
+  sq_pushstring(v, "get_pos_x", -1);
+  sq_newclosure(v, &AmbientSound_get_pos_x_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+  }
+
+  sq_pushstring(v, "get_pos_y", -1);
+  sq_newclosure(v, &AmbientSound_get_pos_y_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'AmbientSound'");
+  }
+
+  // Register class Thunderstorm
+  sq_pushstring(v, "Thunderstorm", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'Thunderstorm'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "start", -1);
+  sq_newclosure(v, &Thunderstorm_start_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start'");
+  }
+
+  sq_pushstring(v, "stop", -1);
+  sq_newclosure(v, &Thunderstorm_stop_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop'");
+  }
+
+  sq_pushstring(v, "thunder", -1);
+  sq_newclosure(v, &Thunderstorm_thunder_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'thunder'");
+  }
+
+  sq_pushstring(v, "lightning", -1);
+  sq_newclosure(v, &Thunderstorm_lightning_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'lightning'");
+  }
+
+  sq_pushstring(v, "flash", -1);
+  sq_newclosure(v, &Thunderstorm_flash_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'flash'");
+  }
+
+  sq_pushstring(v, "electrify", -1);
+  sq_newclosure(v, &Thunderstorm_electrify_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'electrify'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'Thunderstorm'");
+  }
+
+  // Register class TileMap
+  sq_pushstring(v, "TileMap", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'TileMap'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "goto_node", -1);
+  sq_newclosure(v, &TileMap_goto_node_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'goto_node'");
+  }
+
+  sq_pushstring(v, "start_moving", -1);
+  sq_newclosure(v, &TileMap_start_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start_moving'");
+  }
+
+  sq_pushstring(v, "stop_moving", -1);
+  sq_newclosure(v, &TileMap_stop_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+  }
+
+  sq_pushstring(v, "fade", -1);
+  sq_newclosure(v, &TileMap_fade_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'fade'");
+  }
+
+  sq_pushstring(v, "set_alpha", -1);
+  sq_newclosure(v, &TileMap_set_alpha_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_alpha'");
+  }
+
+  sq_pushstring(v, "get_alpha", -1);
+  sq_newclosure(v, &TileMap_get_alpha_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_alpha'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'TileMap'");
+  }
+
+  // Register class SSector
+  sq_pushstring(v, "SSector", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'SSector'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "set_ambient_light", -1);
+  sq_newclosure(v, &SSector_set_ambient_light_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_ambient_light'");
+  }
+
+  sq_pushstring(v, "get_ambient_red", -1);
+  sq_newclosure(v, &SSector_get_ambient_red_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_ambient_red'");
+  }
+
+  sq_pushstring(v, "get_ambient_green", -1);
+  sq_newclosure(v, &SSector_get_ambient_green_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_ambient_green'");
+  }
+
+  sq_pushstring(v, "get_ambient_blue", -1);
+  sq_newclosure(v, &SSector_get_ambient_blue_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_ambient_blue'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'SSector'");
+  }
+
+  // Register class LevelTime
+  sq_pushstring(v, "LevelTime", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'LevelTime'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "start", -1);
+  sq_newclosure(v, &LevelTime_start_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start'");
+  }
+
+  sq_pushstring(v, "stop", -1);
+  sq_newclosure(v, &LevelTime_stop_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop'");
+  }
+
+  sq_pushstring(v, "get_time", -1);
+  sq_newclosure(v, &LevelTime_get_time_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'get_time'");
+  }
+
+  sq_pushstring(v, "set_time", -1);
+  sq_newclosure(v, &LevelTime_set_time_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_time'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'LevelTime'");
+  }
+
+  // Register class WillOWisp
+  sq_pushstring(v, "WillOWisp", -1);
+  if(sq_newclass(v, SQFalse) < 0) {
+    std::ostringstream msg;
+    msg << "Couldn't create new class 'WillOWisp'";
+    throw SquirrelError(v, msg.str());
+  }
+  sq_pushstring(v, "goto_node", -1);
+  sq_newclosure(v, &WillOWisp_goto_node_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'goto_node'");
+  }
+
+  sq_pushstring(v, "set_state", -1);
+  sq_newclosure(v, &WillOWisp_set_state_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'set_state'");
+  }
+
+  sq_pushstring(v, "start_moving", -1);
+  sq_newclosure(v, &WillOWisp_start_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'start_moving'");
+  }
+
+  sq_pushstring(v, "stop_moving", -1);
+  sq_newclosure(v, &WillOWisp_stop_moving_wrapper, 0);
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+  }
+
+  if(SQ_FAILED(sq_createslot(v, -3))) {
+    throw SquirrelError(v, "Couldn't register class 'WillOWisp'");
+  }
+
+}
+
+} // end of namespace Scripting
diff --git a/src/scripting/wrapper.hpp b/src/scripting/wrapper.hpp
new file mode 100644 (file)
index 0000000..9deb6ef
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * WARNING: This file is automatically generated from:
+ *  'src/scripting/wrapper.interface.hpp'
+ * DO NOT CHANGE
+ */
+#ifndef __supertux_WRAPPER_H__
+#define __supertux_WRAPPER_H__
+
+#include <squirrel.h>
+#include "wrapper.interface.hpp"
+
+namespace Scripting
+{
+
+void register_supertux_wrapper(HSQUIRRELVM v);
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::DisplayEffect* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Camera* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Level* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::ScriptedObject* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Text* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Player* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Platform* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Candle* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Wind* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::AmbientSound* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Thunderstorm* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::TileMap* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::SSector* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::LevelTime* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::WillOWisp* object, bool setup_releasehook = false);
+
+}
+
+#endif
diff --git a/src/scripting/wrapper.interface.hpp b/src/scripting/wrapper.interface.hpp
new file mode 100644 (file)
index 0000000..d95dd67
--- /dev/null
@@ -0,0 +1,19 @@
+/* This file is processed by miniswig to produce the scripting API */
+#include "display_effect.hpp"
+#include "camera.hpp"
+#include "level.hpp"
+#include "scripted_object.hpp"
+#include "text.hpp"
+#include "functions.hpp"
+#include "player.hpp"
+#include "floating_image.hpp"
+#include "anchor_points.hpp"
+#include "platform.hpp"
+#include "candle.hpp"
+#include "wind.hpp"
+#include "ambient_sound.hpp"
+#include "thunderstorm.hpp"
+#include "tilemap.hpp"
+#include "ssector.hpp"
+#include "level_time.hpp"
+#include "willowisp.hpp"
diff --git a/src/sector.cpp b/src/sector.cpp
new file mode 100644 (file)
index 0000000..57418fd
--- /dev/null
@@ -0,0 +1,1569 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <memory>
+#include <algorithm>
+#include <stdexcept>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+#include <float.h>
+#include <math.h>
+#include <limits>
+#include <physfs.h>
+
+#include "sector.hpp"
+#include "object/player.hpp"
+#include "object/gameobjs.hpp"
+#include "object/camera.hpp"
+#include "object/background.hpp"
+#include "object/gradient.hpp"
+#include "object/particlesystem.hpp"
+#include "object/particlesystem_interactive.hpp"
+#include "object/tilemap.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/list_iterator.hpp"
+#include "tile.hpp"
+#include "audio/sound_manager.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "collision.hpp"
+#include "spawn_point.hpp"
+#include "math/rect.hpp"
+#include "math/aatriangle.hpp"
+#include "object/coin.hpp"
+#include "object/block.hpp"
+#include "object/invisible_block.hpp"
+#include "object/light.hpp"
+#include "object/pulsing_light.hpp"
+#include "object/bullet.hpp"
+#include "object/text_object.hpp"
+#include "object/portable.hpp"
+#include "badguy/jumpy.hpp"
+#include "trigger/sequence_trigger.hpp"
+#include "player_status.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "script_interface.hpp"
+#include "log.hpp"
+#include "main.hpp"
+
+Sector* Sector::_current = 0;
+
+bool Sector::show_collrects = false;
+bool Sector::draw_solids_only = false;
+
+Sector::Sector(Level* parent)
+  : level(parent), currentmusic(LEVEL_MUSIC),
+  ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
+{
+  add_object(new Player(player_status, "Tux"));
+  add_object(new DisplayEffect("Effect"));
+  add_object(new TextObject("Text"));
+
+  // create a new squirrel table for the sector
+  using namespace Scripting;
+
+  sq_collectgarbage(global_vm);
+
+  sq_newtable(global_vm);
+  sq_pushroottable(global_vm);
+  if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't set sector_table delegate");
+
+  sq_resetobject(&sector_table);
+  if(SQ_FAILED(sq_getstackobj(global_vm, -1, &sector_table)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't get sector table");
+  sq_addref(global_vm, &sector_table);
+  sq_pop(global_vm, 1);
+}
+
+Sector::~Sector()
+{
+  using namespace Scripting;
+
+  deactivate();
+
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ++i) {
+    HSQOBJECT& object = *i;
+    sq_release(global_vm, &object);
+  }
+  sq_release(global_vm, &sector_table);
+  sq_collectgarbage(global_vm);
+
+  update_game_objects();
+  assert(gameobjects_new.size() == 0);
+
+  for(GameObjects::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i) {
+    GameObject* object = *i;
+    before_object_remove(object);
+    object->unref();
+  }
+
+  for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+      ++i)
+    delete *i;
+}
+
+Level*
+Sector::get_level()
+{
+  return level;
+}
+
+GameObject*
+Sector::parse_object(const std::string& name, const lisp::Lisp& reader)
+{
+  if(name == "camera") {
+    Camera* camera = new Camera(this, "Camera");
+    camera->parse(reader);
+    return camera;
+  } else if(name == "particles-snow") {
+    SnowParticleSystem* partsys = new SnowParticleSystem();
+    partsys->parse(reader);
+    return partsys;
+  } else if(name == "particles-rain") {
+    RainParticleSystem* partsys = new RainParticleSystem();
+    partsys->parse(reader);
+    return partsys;
+  } else if(name == "particles-comets") {
+    CometParticleSystem* partsys = new CometParticleSystem();
+    partsys->parse(reader);
+    return partsys;
+  } else if(name == "particles-ghosts") {
+    GhostParticleSystem* partsys = new GhostParticleSystem();
+    partsys->parse(reader);
+    return partsys;
+  } else if(name == "particles-clouds") {
+    CloudParticleSystem* partsys = new CloudParticleSystem();
+    partsys->parse(reader);
+    return partsys;
+  } else if(name == "money") { // for compatibility with old maps
+    return new Jumpy(reader);
+  }
+
+  try {
+    return create_object(name, reader);
+  } catch(std::exception& e) {
+    log_warning << e.what() << "" << std::endl;
+  }
+
+  return 0;
+}
+
+void
+Sector::parse(const lisp::Lisp& sector)
+{
+  bool has_background = false;
+  lisp::ListIterator iter(&sector);
+  while(iter.next()) {
+    const std::string& token = iter.item();
+    if(token == "name") {
+      iter.value()->get(name);
+    } else if(token == "gravity") {
+      iter.value()->get(gravity);
+    } else if(token == "music") {
+      iter.value()->get(music);
+    } else if(token == "spawnpoint") {
+      SpawnPoint* sp = new SpawnPoint(iter.lisp());
+      spawnpoints.push_back(sp);
+    } else if(token == "init-script") {
+      iter.value()->get(init_script);
+    } else if(token == "ambient-light") {
+      std::vector<float> vColor;
+      sector.get_vector( "ambient-light", vColor );
+      if(vColor.size() < 3) {
+        log_warning << "(ambient-light) requires a color as argument" << std::endl;
+      } else {
+        ambient_light = Color( vColor );
+      }
+    } else {
+      GameObject* object = parse_object(token, *(iter.lisp()));
+      if(object) {
+        if(dynamic_cast<Background *>(object)) {
+           has_background = true;
+        } else if(dynamic_cast<Gradient *>(object)) {
+           has_background = true;
+        }
+        add_object(object);
+      }
+    }
+  }
+
+  if(!has_background) {
+    Gradient* gradient = new Gradient();
+    gradient->set_gradient(Color(0.3, 0.4, 0.75), Color(1, 1, 1));
+    add_object(gradient);
+  }
+
+  update_game_objects();
+
+  if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
+
+  fix_old_tiles();
+  if(!camera) {
+    log_warning << "sector '" << name << "' does not contain a camera." << std::endl;
+    update_game_objects();
+    add_object(new Camera(this, "Camera"));
+  }
+
+  update_game_objects();
+}
+
+void
+Sector::parse_old_format(const lisp::Lisp& reader)
+{
+  name = "main";
+  reader.get("gravity", gravity);
+
+  std::string backgroundimage;
+  if (reader.get("background", backgroundimage) && (backgroundimage != "")) {
+    if (backgroundimage == "arctis.png") backgroundimage = "arctis.jpg";
+    if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg";
+    if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg";
+    backgroundimage = "images/background/" + backgroundimage;
+    if (!PHYSFS_exists(backgroundimage.c_str())) {
+      log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl;
+      backgroundimage = "";
+    }
+  }
+
+  float bgspeed = .5;
+  reader.get("bkgd_speed", bgspeed);
+  bgspeed /= 100;
+
+  Color bkgd_top, bkgd_bottom;
+  int r = 0, g = 0, b = 128;
+  reader.get("bkgd_red_top", r);
+  reader.get("bkgd_green_top",  g);
+  reader.get("bkgd_blue_top",  b);
+  bkgd_top.red = static_cast<float> (r) / 255.0f;
+  bkgd_top.green = static_cast<float> (g) / 255.0f;
+  bkgd_top.blue = static_cast<float> (b) / 255.0f;
+
+  reader.get("bkgd_red_bottom",  r);
+  reader.get("bkgd_green_bottom", g);
+  reader.get("bkgd_blue_bottom", b);
+  bkgd_bottom.red = static_cast<float> (r) / 255.0f;
+  bkgd_bottom.green = static_cast<float> (g) / 255.0f;
+  bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
+
+  if(backgroundimage != "") {
+    Background* background = new Background();
+    background->set_image(backgroundimage, bgspeed);
+    add_object(background);
+  } else {
+    Gradient* gradient = new Gradient();
+    gradient->set_gradient(bkgd_top, bkgd_bottom);
+    add_object(gradient);
+  }
+
+  std::string particlesystem;
+  reader.get("particle_system", particlesystem);
+  if(particlesystem == "clouds")
+    add_object(new CloudParticleSystem());
+  else if(particlesystem == "snow")
+    add_object(new SnowParticleSystem());
+  else if(particlesystem == "rain")
+    add_object(new RainParticleSystem());
+
+  Vector startpos(100, 170);
+  reader.get("start_pos_x", startpos.x);
+  reader.get("start_pos_y", startpos.y);
+
+  SpawnPoint* spawn = new SpawnPoint;
+  spawn->pos = startpos;
+  spawn->name = "main";
+  spawnpoints.push_back(spawn);
+
+  music = "chipdisko.ogg";
+  // skip reading music filename. It's all .ogg now, anyway
+  /*
+  reader.get("music", music);
+  */
+  music = "music/" + music;
+
+  int width = 30, height = 15;
+  reader.get("width", width);
+  reader.get("height", height);
+
+  std::vector<unsigned int> tiles;
+  if(reader.get_vector("interactive-tm", tiles)
+      || reader.get_vector("tilemap", tiles)) {
+    TileMap* tilemap = new TileMap();
+    tilemap->set(width, height, tiles, LAYER_TILES, true);
+
+    // replace tile id 112 (old invisible tile) with 1311 (new invisible tile)
+    for(size_t x=0; x < tilemap->get_width(); ++x) {
+      for(size_t y=0; y < tilemap->get_height(); ++y) {
+        const Tile* tile = tilemap->get_tile(x, y);
+        if(tile->getID() == 112) tilemap->change(x, y, 1311);
+      }
+    }
+
+    if (height < 19) tilemap->resize(width, 19);
+    add_object(tilemap);
+  }
+
+  if(reader.get_vector("background-tm", tiles)) {
+    TileMap* tilemap = new TileMap();
+    tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
+    if (height < 19) tilemap->resize(width, 19);
+    add_object(tilemap);
+  }
+
+  if(reader.get_vector("foreground-tm", tiles)) {
+    TileMap* tilemap = new TileMap();
+    tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
+
+    // fill additional space in foreground with tiles of ID 2035 (lightmap/black)
+    if (height < 19) tilemap->resize(width, 19, 2035);
+
+    add_object(tilemap);
+  }
+
+  // read reset-points (now spawn-points)
+  const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
+  if(resetpoints) {
+    lisp::ListIterator iter(resetpoints);
+    while(iter.next()) {
+      if(iter.item() == "point") {
+        Vector sp_pos;
+        if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
+          {
+          SpawnPoint* sp = new SpawnPoint;
+          sp->name = "main";
+          sp->pos = sp_pos;
+          spawnpoints.push_back(sp);
+          }
+      } else {
+        log_warning << "Unknown token '" << iter.item() << "' in reset-points." << std::endl;
+      }
+    }
+  }
+
+  // read objects
+  const lisp::Lisp* objects = reader.get_lisp("objects");
+  if(objects) {
+    lisp::ListIterator iter(objects);
+    while(iter.next()) {
+      GameObject* object = parse_object(iter.item(), *(iter.lisp()));
+      if(object) {
+        add_object(object);
+      } else {
+        log_warning << "Unknown object '" << iter.item() << "' in level." << std::endl;
+      }
+    }
+  }
+
+  // add a camera
+  Camera* camera = new Camera(this, "Camera");
+  add_object(camera);
+
+  update_game_objects();
+
+  if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
+
+  fix_old_tiles();
+  update_game_objects();
+}
+
+void
+Sector::fix_old_tiles()
+{
+  for(std::list<TileMap*>::iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    for(size_t x=0; x < solids->get_width(); ++x) {
+      for(size_t y=0; y < solids->get_height(); ++y) {
+       const Tile* tile = solids->get_tile(x, y);
+       Vector pos(solids->get_x_offset() + x*32, solids->get_y_offset() + y*32);
+
+       if(tile->getID() == 112) {
+         add_object(new InvisibleBlock(pos));
+         solids->change(x, y, 0);
+       } else if(tile->getAttributes() & Tile::COIN) {
+         add_object(new Coin(pos));
+         solids->change(x, y, 0);
+       } else if(tile->getAttributes() & Tile::FULLBOX) {
+         add_object(new BonusBlock(pos, tile->getData()));
+         solids->change(x, y, 0);
+       } else if(tile->getAttributes() & Tile::BRICK) {
+         add_object(new Brick(pos, tile->getData()));
+         solids->change(x, y, 0);
+       } else if(tile->getAttributes() & Tile::GOAL) {
+         std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
+         add_object(new SequenceTrigger(pos, sequence));
+         solids->change(x, y, 0);
+       }
+      }
+    }
+  }
+
+  // add lights for special tiles
+  for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); i++) {
+    TileMap* tm = dynamic_cast<TileMap*>(*i);
+    if (!tm) continue;
+    for(size_t x=0; x < tm->get_width(); ++x) {
+      for(size_t y=0; y < tm->get_height(); ++y) {
+       const Tile* tile = tm->get_tile(x, y);
+       Vector pos(tm->get_x_offset() + x*32, tm->get_y_offset() + y*32);
+       Vector center(pos.x + 16, pos.y + 16);
+
+       // torch
+       if (tile->getID() == 1517) {
+         float pseudo_rnd = (float)((int)pos.x % 10) / 10;
+         add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.9f, 1.0f, Color(1.0f, 1.0f, 0.6f, 1.0f)));
+       }
+       // lava or lavaflow
+       if ((tile->getID() == 173) || (tile->getID() == 1700) || (tile->getID() == 1705) || (tile->getID() == 1706)) {
+         // space lights a bit
+         if (((tm->get_tile(x-1, y)->getID() != tm->get_tile(x,y)->getID())
+             && (tm->get_tile(x, y-1)->getID() != tm->get_tile(x,y)->getID()))
+             || ((x % 3 == 0) && (y % 3 == 0))) {
+           float pseudo_rnd = (float)((int)pos.x % 10) / 10;
+           add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.8f, 1.0f, Color(1.0f, 0.3f, 0.0f, 1.0f)));
+         }
+       }
+
+      }
+    }
+  }
+
+
+}
+
+void
+Sector::write(lisp::Writer& writer)
+{
+  writer.write_string("name", name);
+  writer.write_float("gravity", gravity);
+  writer.write_string("music", music);
+
+  // write spawnpoints
+  for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+      ++i) {
+    SpawnPoint* spawn = *i;
+    writer.start_list("spawn-points");
+    writer.write_string("name", spawn->name);
+    writer.write_float("x", spawn->pos.x);
+    writer.write_float("y", spawn->pos.y);
+    writer.end_list("spawn-points");
+  }
+
+  // write objects
+  for(GameObjects::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i) {
+    Serializable* serializable = dynamic_cast<Serializable*> (*i);
+    if(serializable)
+      serializable->write(writer);
+  }
+}
+
+HSQUIRRELVM
+Sector::run_script(std::istream& in, const std::string& sourcename)
+{
+  using namespace Scripting;
+
+  // garbage collect thread list
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ) {
+    HSQOBJECT& object = *i;
+    HSQUIRRELVM vm = object_to_vm(object);
+
+    if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+      sq_release(global_vm, &object);
+      i = scripts.erase(i);
+      continue;
+    }
+
+    ++i;
+  }
+
+  HSQOBJECT object = create_thread(global_vm);
+  scripts.push_back(object);
+
+  HSQUIRRELVM vm = object_to_vm(object);
+
+  // set sector_table as roottable for the thread
+  sq_pushobject(vm, sector_table);
+  sq_setroottable(vm);
+
+  compile_and_run(vm, in, sourcename);
+
+  return vm;
+}
+
+void
+Sector::add_object(GameObject* object)
+{
+  // make sure the object isn't already in the list
+#ifdef DEBUG
+  for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
+      ++i) {
+    if(*i == object) {
+      assert("object already added to sector" == 0);
+    }
+  }
+  for(GameObjects::iterator i = gameobjects_new.begin();
+      i != gameobjects_new.end(); ++i) {
+    if(*i == object) {
+      assert("object already added to sector" == 0);
+    }
+  }
+#endif
+
+  object->ref();
+  gameobjects_new.push_back(object);
+}
+
+void
+Sector::activate(const std::string& spawnpoint)
+{
+  SpawnPoint* sp = 0;
+  for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+      ++i) {
+    if((*i)->name == spawnpoint) {
+      sp = *i;
+      break;
+    }
+  }
+  if(!sp) {
+    log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
+    if(spawnpoint != "main") {
+      activate("main");
+    } else {
+      activate(Vector(0, 0));
+    }
+  } else {
+    activate(sp->pos);
+  }
+}
+
+void
+Sector::activate(const Vector& player_pos)
+{
+  if(_current != this) {
+    if(_current != NULL)
+      _current->deactivate();
+    _current = this;
+
+    // register sectortable as sector in scripting
+    HSQUIRRELVM vm = Scripting::global_vm;
+    sq_pushroottable(vm);
+    sq_pushstring(vm, "sector", -1);
+    sq_pushobject(vm, sector_table);
+    if(SQ_FAILED(sq_createslot(vm, -3)))
+      throw Scripting::SquirrelError(vm, "Couldn't set sector in roottable");
+    sq_pop(vm, 1);
+
+    for(GameObjects::iterator i = gameobjects.begin();
+        i != gameobjects.end(); ++i) {
+      GameObject* object = *i;
+
+      try_expose(object);
+    }
+  }
+  try_expose_me();
+
+  // spawn smalltux below spawnpoint
+  if (!player->is_big()) {
+    player->move(player_pos + Vector(0,32));
+  } else {
+    player->move(player_pos);
+  }
+
+  // spawning tux in the ground would kill him
+  if(!is_free_of_tiles(player->get_bbox())) {
+    log_warning << "Tried spawning Tux in solid matter. Compensating." << std::endl;
+    Vector npos = player->get_bbox().p1;
+    npos.y-=32;
+    player->move(npos);
+  }
+
+  camera->reset(player->get_pos());
+  update_game_objects();
+
+  // Run init script
+  if(init_script != "") {
+    std::istringstream in(init_script);
+    run_script(in, std::string("Sector(") + name + ") - init");
+  }
+}
+
+void
+Sector::deactivate()
+{
+  if(_current != this)
+    return;
+
+  // remove sector entry from global vm
+  HSQUIRRELVM vm = Scripting::global_vm;
+  sq_pushroottable(vm);
+  sq_pushstring(vm, "sector", -1);
+  if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+    throw Scripting::SquirrelError(vm, "Couldn't unset sector in roottable");
+  sq_pop(vm, 1);
+
+  for(GameObjects::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i) {
+    GameObject* object = *i;
+
+    try_unexpose(object);
+  }
+
+  try_unexpose_me();
+  _current = NULL;
+}
+
+Rect
+Sector::get_active_region()
+{
+  return Rect(
+    camera->get_translation() - Vector(1600, 1200),
+    camera->get_translation() + Vector(1600, 1200) + Vector(SCREEN_WIDTH,SCREEN_HEIGHT));
+}
+
+void
+Sector::update(float elapsed_time)
+{
+  player->check_bounds(camera);
+
+  /* update objects */
+  for(GameObjects::iterator i = gameobjects.begin();
+          i != gameobjects.end(); ++i) {
+    GameObject* object = *i;
+    if(!object->is_valid())
+      continue;
+
+    object->update(elapsed_time);
+  }
+
+  /* Handle all possible collisions. */
+  handle_collisions();
+  update_game_objects();
+}
+
+void
+Sector::update_game_objects()
+{
+  /** cleanup marked objects */
+  for(std::vector<GameObject*>::iterator i = gameobjects.begin();
+      i != gameobjects.end(); /* nothing */) {
+    GameObject* object = *i;
+
+    if(object->is_valid()) {
+      ++i;
+      continue;
+    }
+
+    before_object_remove(object);
+
+    object->unref();
+    i = gameobjects.erase(i);
+  }
+
+  /* add newly created objects */
+  for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
+      i != gameobjects_new.end(); ++i)
+  {
+    GameObject* object = *i;
+
+    before_object_add(object);
+
+    gameobjects.push_back(object);
+  }
+  gameobjects_new.clear();
+
+  /* update solid_tilemaps list */
+  //FIXME: this could be more efficient
+  solid_tilemaps.clear();
+  for(std::vector<GameObject*>::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i)
+  {
+    TileMap* tm = dynamic_cast<TileMap*>(*i);
+    if (!tm) continue;
+    if (tm->is_solid()) solid_tilemaps.push_back(tm);
+  }
+
+}
+
+bool
+Sector::before_object_add(GameObject* object)
+{
+  Bullet* bullet = dynamic_cast<Bullet*> (object);
+  if(bullet != NULL) {
+    bullets.push_back(bullet);
+  }
+
+  MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+  if(movingobject != NULL) {
+    moving_objects.push_back(movingobject);
+  }
+
+  Portable* portable = dynamic_cast<Portable*> (object);
+  if(portable != NULL) {
+    portables.push_back(portable);
+  }
+
+  TileMap* tilemap = dynamic_cast<TileMap*> (object);
+  if(tilemap != NULL && tilemap->is_solid()) {
+    solid_tilemaps.push_back(tilemap);
+  }
+
+  Camera* camera = dynamic_cast<Camera*> (object);
+  if(camera != NULL) {
+    if(this->camera != 0) {
+      log_warning << "Multiple cameras added. Ignoring" << std::endl;
+      return false;
+    }
+    this->camera = camera;
+  }
+
+  Player* player = dynamic_cast<Player*> (object);
+  if(player != NULL) {
+    if(this->player != 0) {
+      log_warning << "Multiple players added. Ignoring" << std::endl;
+      return false;
+    }
+    this->player = player;
+  }
+
+  UsesPhysic *physic_object = dynamic_cast<UsesPhysic *>(object);
+  if(physic_object)
+  {
+    physic_object->physic.set_gravity(gravity);
+  }
+
+
+  if(_current == this) {
+    try_expose(object);
+  }
+
+  return true;
+}
+
+void
+Sector::try_expose(GameObject* object)
+{
+  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+  if(interface != NULL) {
+    HSQUIRRELVM vm = Scripting::global_vm;
+    sq_pushobject(vm, sector_table);
+    interface->expose(vm, -1);
+    sq_pop(vm, 1);
+  }
+}
+
+void
+Sector::try_expose_me()
+{
+  HSQUIRRELVM vm = Scripting::global_vm;
+  sq_pushobject(vm, sector_table);
+  Scripting::SSector* interface = static_cast<Scripting::SSector*> (this);
+  expose_object(vm, -1, interface, "settings", false);
+  sq_pop(vm, 1);
+}
+
+void
+Sector::before_object_remove(GameObject* object)
+{
+  Portable* portable = dynamic_cast<Portable*> (object);
+  if(portable != NULL) {
+    portables.erase(std::find(portables.begin(), portables.end(), portable));
+  }
+  Bullet* bullet = dynamic_cast<Bullet*> (object);
+  if(bullet != NULL) {
+    bullets.erase(std::find(bullets.begin(), bullets.end(), bullet));
+  }
+  MovingObject* moving_object = dynamic_cast<MovingObject*> (object);
+  if(moving_object != NULL) {
+    moving_objects.erase(
+        std::find(moving_objects.begin(), moving_objects.end(), moving_object));
+  }
+
+  if(_current == this)
+    try_unexpose(object);
+}
+
+void
+Sector::try_unexpose(GameObject* object)
+{
+  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+  if(interface != NULL) {
+    HSQUIRRELVM vm = Scripting::global_vm;
+    SQInteger oldtop = sq_gettop(vm);
+    sq_pushobject(vm, sector_table);
+    try {
+      interface->unexpose(vm, -1);
+    } catch(std::exception& e) {
+      log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+    }
+    sq_settop(vm, oldtop);
+  }
+}
+
+void
+Sector::try_unexpose_me()
+{
+  HSQUIRRELVM vm = Scripting::global_vm;
+  SQInteger oldtop = sq_gettop(vm);
+  sq_pushobject(vm, sector_table);
+  try {
+    Scripting::unexpose_object(vm, -1, "settings");
+  } catch(std::exception& e) {
+    log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+  }
+  sq_settop(vm, oldtop);
+}
+void
+Sector::draw(DrawingContext& context)
+{
+  context.set_ambient_color( ambient_light );
+  context.push_transform();
+  context.set_translation(camera->get_translation());
+
+  for(GameObjects::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i) {
+    GameObject* object = *i;
+    if(!object->is_valid())
+      continue;
+
+    if (draw_solids_only)
+    {
+      TileMap* tm = dynamic_cast<TileMap*>(object);
+      if (tm && !tm->is_solid())
+        continue;
+    }
+
+    object->draw(context);
+  }
+
+  if(show_collrects) {
+    Color col(0.2f, 0.2f, 0.2f, 0.7f);
+    for(MovingObjects::iterator i = moving_objects.begin();
+            i != moving_objects.end(); ++i) {
+      MovingObject* object = *i;
+      const Rect& rect = object->get_bbox();
+
+      context.draw_filled_rect(rect, col, LAYER_FOREGROUND1 + 10);
+    }
+  }
+
+  context.pop_transform();
+}
+
+/*-------------------------------------------------------------------------
+ * Collision Detection
+ *-------------------------------------------------------------------------*/
+
+static const float SHIFT_DELTA = 7.0f;
+
+/** r1 is supposed to be moving, r2 a solid object */
+void check_collisions(collision::Constraints* constraints,
+                      const Vector& movement, const Rect& r1, const Rect& r2,
+                      GameObject* object = NULL, MovingObject* other = NULL)
+{
+  if(!collision::intersects(r1, r2))
+    return;
+
+  MovingObject *moving_object = dynamic_cast<MovingObject*> (object);
+  CollisionHit dummy;
+  if(other != NULL && !other->collides(*object, dummy))
+    return;
+  if(moving_object != NULL && !moving_object->collides(*other, dummy))
+    return;
+
+  // calculate intersection
+  float itop    = r1.get_bottom() - r2.get_top();
+  float ibottom = r2.get_bottom() - r1.get_top();
+  float ileft   = r1.get_right() - r2.get_left();
+  float iright  = r2.get_right() - r1.get_left();
+
+  if(fabsf(movement.y) > fabsf(movement.x)) {
+    if(ileft < SHIFT_DELTA) {
+      constraints->right = std::min(constraints->right, r2.get_left());
+      return;
+    } else if(iright < SHIFT_DELTA) {
+      constraints->left = std::max(constraints->left, r2.get_right());
+      return;
+    }
+  } else {
+    // shiftout bottom/top
+    if(itop < SHIFT_DELTA) {
+      constraints->bottom = std::min(constraints->bottom, r2.get_top());
+      return;
+    } else if(ibottom < SHIFT_DELTA) {
+      constraints->top = std::max(constraints->top, r2.get_bottom());
+      return;
+    }
+  }
+
+  if(other != NULL) {
+    HitResponse response = other->collision(*object, dummy);
+    if(response == PASSTHROUGH)
+      return;
+
+    if(other->get_movement() != Vector(0, 0)) {
+      // TODO what todo when we collide with 2 moving objects?!?
+      constraints->ground_movement = other->get_movement();
+    }
+  }
+
+  float vert_penetration = std::min(itop, ibottom);
+  float horiz_penetration = std::min(ileft, iright);
+  if(vert_penetration < horiz_penetration) {
+    if(itop < ibottom) {
+      constraints->bottom = std::min(constraints->bottom, r2.get_top());
+      constraints->hit.bottom = true;
+    } else {
+      constraints->top = std::max(constraints->top, r2.get_bottom());
+      constraints->hit.top = true;
+    }
+  } else {
+    if(ileft < iright) {
+      constraints->right = std::min(constraints->right, r2.get_left());
+      constraints->hit.right = true;
+    } else {
+      constraints->left = std::max(constraints->left, r2.get_right());
+      constraints->hit.left = true;
+    }
+  }
+}
+
+static const float DELTA = .001f;
+
+void
+Sector::collision_tilemap(collision::Constraints* constraints,
+                          const Vector& movement, const Rect& dest) const
+{
+  // calculate rectangle where the object will move
+  float x1 = dest.get_left();
+  float x2 = dest.get_right();
+  float y1 = dest.get_top();
+  float y2 = dest.get_bottom();
+
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+
+    // test with all tiles in this rectangle
+    int starttilex = int(x1 - solids->get_x_offset()) / 32;
+    int starttiley = int(y1 - solids->get_y_offset()) / 32;
+    int max_x = int(x2 - solids->get_x_offset());
+    int max_y = int(y2+1 - solids->get_y_offset());
+
+    for(int x = starttilex; x*32 < max_x; ++x) {
+      for(int y = starttiley; y*32 < max_y; ++y) {
+       const Tile* tile = solids->get_tile(x, y);
+       if(!tile)
+         continue;
+       // skip non-solid tiles
+       if((tile->getAttributes() & Tile::SOLID) == 0)
+         continue;
+       // only handle unisolid when the player is falling down and when he was
+       // above the tile before
+       if(tile->getAttributes() & Tile::UNISOLID) {
+         if(movement.y <= 0 || dest.get_bottom() - movement.y - SHIFT_DELTA > y*32)
+           continue;
+       }
+
+       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
+         AATriangle triangle;
+         Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
+         Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+         triangle = AATriangle(p1, p2, tile->getData());
+
+         collision::rectangle_aatriangle(constraints, dest, triangle);
+       } else { // normal rectangular tile
+         Rect rect(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset(), (x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+         check_collisions(constraints, movement, dest, rect);
+       }
+      }
+    }
+  }
+}
+
+uint32_t
+Sector::collision_tile_attributes(const Rect& dest) const
+{
+  float x1 = dest.p1.x;
+  float y1 = dest.p1.y;
+  float x2 = dest.p2.x;
+  float y2 = dest.p2.y;
+
+  uint32_t result = 0;
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+
+    // test with all tiles in this rectangle
+    int starttilex = int(x1 - solids->get_x_offset()) / 32;
+    int starttiley = int(y1 - solids->get_y_offset()) / 32;
+    int max_x = int(x2 - solids->get_x_offset());
+    int max_y = int(y2+1 - solids->get_y_offset());
+
+    for(int x = starttilex; x*32 < max_x; ++x) {
+      for(int y = starttiley; y*32 < max_y; ++y) {
+       const Tile* tile = solids->get_tile(x, y);
+       if(!tile)
+         continue;
+       result |= tile->getAttributes();
+      }
+    }
+  }
+
+  return result;
+}
+
+/** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
+static void get_hit_normal(const Rect& r1, const Rect& r2, CollisionHit& hit,
+                           Vector& normal)
+{
+  float itop = r1.get_bottom() - r2.get_top();
+  float ibottom = r2.get_bottom() - r1.get_top();
+  float ileft = r1.get_right() - r2.get_left();
+  float iright = r2.get_right() - r1.get_left();
+
+  float vert_penetration = std::min(itop, ibottom);
+  float horiz_penetration = std::min(ileft, iright);
+  if(vert_penetration < horiz_penetration) {
+    if(itop < ibottom) {
+      hit.bottom = true;
+      normal.y = vert_penetration;
+    } else {
+      hit.top = true;
+      normal.y = -vert_penetration;
+    }
+  } else {
+    if(ileft < iright) {
+      hit.right = true;
+      normal.x = horiz_penetration;
+    } else {
+      hit.left = true;
+      normal.x = -horiz_penetration;
+    }
+  }
+}
+
+void
+Sector::collision_object(MovingObject* object1, MovingObject* object2) const
+{
+  using namespace collision;
+
+  const Rect& r1 = object1->dest;
+  const Rect& r2 = object2->dest;
+
+  CollisionHit hit;
+  if(intersects(object1->dest, object2->dest)) {
+    Vector normal;
+    get_hit_normal(r1, r2, hit, normal);
+
+    if(!object1->collides(*object2, hit))
+      return;
+    std::swap(hit.left, hit.right);
+    std::swap(hit.top, hit.bottom);
+    if(!object2->collides(*object1, hit))
+      return;
+    std::swap(hit.left, hit.right);
+    std::swap(hit.top, hit.bottom);
+
+    HitResponse response1 = object1->collision(*object2, hit);
+    std::swap(hit.left, hit.right);
+    std::swap(hit.top, hit.bottom);
+    HitResponse response2 = object2->collision(*object1, hit);
+    if(response1 == CONTINUE && response2 == CONTINUE) {
+      normal *= (0.5 + DELTA);
+      object1->dest.move(-normal);
+      object2->dest.move(normal);
+    } else if (response1 == CONTINUE && response2 == FORCE_MOVE) {
+      normal *= (1 + DELTA);
+      object1->dest.move(-normal);
+    } else if (response1 == FORCE_MOVE && response2 == CONTINUE) {
+      normal *= (1 + DELTA);
+      object2->dest.move(normal);
+    }
+  }
+}
+
+void
+Sector::collision_static(collision::Constraints* constraints,
+                         const Vector& movement, const Rect& dest,
+                         GameObject& object)
+{
+  collision_tilemap(constraints, movement, dest);
+
+  // collision with other (static) objects
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+    if(moving_object->get_group() != COLGROUP_STATIC
+       && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+      continue;
+    if(!moving_object->is_valid())
+      continue;
+
+    if(moving_object != &object)
+      check_collisions(constraints, movement, dest, moving_object->bbox,
+          &object, moving_object);
+  }
+}
+
+void
+Sector::collision_static_constrains(MovingObject& object)
+{
+  using namespace collision;
+  float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+
+  Constraints constraints;
+  Vector movement = object.get_movement();
+  Rect& dest = object.dest;
+  float owidth = object.get_bbox().get_width();
+  float oheight = object.get_bbox().get_height();
+
+  for(int i = 0; i < 2; ++i) {
+    collision_static(&constraints, Vector(0, movement.y), dest, object);
+    if(!constraints.has_constraints())
+      break;
+
+    // apply calculated horizontal constraints
+    if(constraints.bottom < infinity) {
+      float height = constraints.bottom - constraints.top;
+      if(height < oheight) {
+        // we're crushed, but ignore this for now, we'll get this again
+        // later if we're really crushed or things will solve itself when
+        // looking at the vertical constraints
+      }
+      dest.p2.y = constraints.bottom - DELTA;
+      dest.p1.y = dest.p2.y - oheight;
+    } else if(constraints.top > -infinity) {
+      dest.p1.y = constraints.top + DELTA;
+      dest.p2.y = dest.p1.y + oheight;
+    }
+  }
+  if(constraints.has_constraints()) {
+    if(constraints.hit.bottom) {
+      dest.move(constraints.ground_movement);
+    }
+    if(constraints.hit.top || constraints.hit.bottom) {
+      constraints.hit.left = false;
+      constraints.hit.right = false;
+      object.collision_solid(constraints.hit);
+    }
+  }
+
+  constraints = Constraints();
+  for(int i = 0; i < 2; ++i) {
+    collision_static(&constraints, movement, dest, object);
+    if(!constraints.has_constraints())
+      break;
+
+    // apply calculated vertical constraints
+    if(constraints.right < infinity) {
+      float width = constraints.right - constraints.left;
+      if(width + SHIFT_DELTA < owidth) {
+#if 0
+        printf("Object %p crushed horizontally... L:%f R:%f\n", &object,
+            constraints.left, constraints.right);
+#endif
+        CollisionHit h;
+        h.left = true;
+        h.right = true;
+        h.crush = true;
+        object.collision_solid(h);
+      } else {
+        dest.p2.x = constraints.right - DELTA;
+        dest.p1.x = dest.p2.x - owidth;
+      }
+    } else if(constraints.left > -infinity) {
+      dest.p1.x = constraints.left + DELTA;
+      dest.p2.x = dest.p1.x + owidth;
+    }
+  }
+
+  if(constraints.has_constraints()) {
+    if( constraints.hit.left || constraints.hit.right
+        || constraints.hit.top || constraints.hit.bottom
+        || constraints.hit.crush )
+      object.collision_solid(constraints.hit);
+  }
+
+  // an extra pass to make sure we're not crushed horizontally
+  constraints = Constraints();
+  collision_static(&constraints, movement, dest, object);
+  if(constraints.bottom < infinity) {
+    float height = constraints.bottom - constraints.top;
+    if(height + SHIFT_DELTA < oheight) {
+#if 0
+      printf("Object %p crushed vertically...\n", &object);
+#endif
+      CollisionHit h;
+      h.top = true;
+      h.bottom = true;
+      h.crush = true;
+      object.collision_solid(h);
+    }
+  }
+}
+
+namespace {
+  const float MAX_SPEED = 16.0f;
+}
+
+void
+Sector::handle_collisions()
+{
+  using namespace collision;
+
+  // calculate destination positions of the objects
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+    Vector mov = moving_object->get_movement();
+
+    // make sure movement is never faster than MAX_SPEED. Norm is pretty fat, so two addl. checks are done before.
+    if (((mov.x > MAX_SPEED * M_SQRT1_2) || (mov.y > MAX_SPEED * M_SQRT1_2)) && (mov.norm() > MAX_SPEED)) {
+      moving_object->movement = mov.unit() * MAX_SPEED;
+      //log_debug << "Temporarily reduced object's speed of " << mov.norm() << " to " << moving_object->movement.norm() << "." << std::endl;
+    }
+
+    moving_object->dest = moving_object->get_bbox();
+    moving_object->dest.move(moving_object->get_movement());
+  }
+
+  // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+    if((moving_object->get_group() != COLGROUP_MOVING
+          && moving_object->get_group() != COLGROUP_MOVING_STATIC
+          && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
+        || !moving_object->is_valid())
+      continue;
+
+    collision_static_constrains(*moving_object);
+  }
+
+
+  // part2: COLGROUP_MOVING vs tile attributes
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+    if((moving_object->get_group() != COLGROUP_MOVING
+          && moving_object->get_group() != COLGROUP_MOVING_STATIC
+          && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
+        || !moving_object->is_valid())
+      continue;
+
+    uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
+    if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
+      moving_object->collision_tile(tile_attributes);
+    }
+  }
+
+  // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+    if((moving_object->get_group() != COLGROUP_MOVING
+          && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+        || !moving_object->is_valid())
+      continue;
+
+    for(MovingObjects::iterator i2 = moving_objects.begin();
+        i2 != moving_objects.end(); ++i2) {
+      MovingObject* moving_object_2 = *i2;
+      if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
+         || !moving_object_2->is_valid())
+        continue;
+
+      if(intersects(moving_object->dest, moving_object_2->dest)) {
+        Vector normal;
+        CollisionHit hit;
+        get_hit_normal(moving_object->dest, moving_object_2->dest,
+                       hit, normal);
+        if(!moving_object->collides(*moving_object_2, hit))
+          continue;
+        if(!moving_object_2->collides(*moving_object, hit))
+          continue;
+
+        moving_object->collision(*moving_object_2, hit);
+        moving_object_2->collision(*moving_object, hit);
+      }
+    }
+  }
+
+  // part3: COLGROUP_MOVING vs COLGROUP_MOVING
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+
+    if((moving_object->get_group() != COLGROUP_MOVING
+          && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+        || !moving_object->is_valid())
+      continue;
+
+    for(MovingObjects::iterator i2 = i+1;
+        i2 != moving_objects.end(); ++i2) {
+      MovingObject* moving_object_2 = *i2;
+      if((moving_object_2->get_group() != COLGROUP_MOVING
+            && moving_object_2->get_group() != COLGROUP_MOVING_STATIC)
+         || !moving_object_2->is_valid())
+        continue;
+
+      collision_object(moving_object, moving_object_2);
+    }
+  }
+
+  // apply object movement
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+
+    moving_object->bbox = moving_object->dest;
+    moving_object->movement = Vector(0, 0);
+  }
+}
+
+bool
+Sector::is_free_of_tiles(const Rect& rect, const bool ignoreUnisolid) const
+{
+  using namespace collision;
+
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+
+    // test with all tiles in this rectangle
+    int starttilex = int(rect.p1.x - solids->get_x_offset()) / 32;
+    int starttiley = int(rect.p1.y - solids->get_y_offset()) / 32;
+    int max_x = int(rect.p2.x - solids->get_x_offset());
+    int max_y = int(rect.p2.y - solids->get_y_offset());
+
+    for(int x = starttilex; x*32 <= max_x; ++x) {
+      for(int y = starttiley; y*32 <= max_y; ++y) {
+       const Tile* tile = solids->get_tile(x, y);
+       if(!tile) continue;
+       if(tile->getAttributes() & Tile::SLOPE) {
+         AATriangle triangle;
+         Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
+         Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+         triangle = AATriangle(p1, p2, tile->getData());
+         Constraints constraints;
+         return collision::rectangle_aatriangle(&constraints, rect, triangle);
+       }
+       if((tile->getAttributes() & Tile::SOLID) && !ignoreUnisolid) return false;
+       if((tile->getAttributes() & Tile::SOLID) && !(tile->getAttributes() & Tile::UNISOLID)) return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool
+Sector::is_free_of_statics(const Rect& rect, const MovingObject* ignore_object, const bool ignoreUnisolid) const
+{
+  using namespace collision;
+
+  if (!is_free_of_tiles(rect, ignoreUnisolid)) return false;
+
+  for(MovingObjects::const_iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    const MovingObject* moving_object = *i;
+    if (moving_object == ignore_object) continue;
+    if (!moving_object->is_valid()) continue;
+    if (moving_object->get_group() == COLGROUP_STATIC) {
+      if(intersects(rect, moving_object->get_bbox())) return false;
+    }
+  }
+
+  return true;
+}
+
+bool
+Sector::is_free_of_movingstatics(const Rect& rect, const MovingObject* ignore_object) const
+{
+  using namespace collision;
+
+  if (!is_free_of_tiles(rect)) return false;
+
+  for(MovingObjects::const_iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    const MovingObject* moving_object = *i;
+    if (moving_object == ignore_object) continue;
+    if (!moving_object->is_valid()) continue;
+    if ((moving_object->get_group() == COLGROUP_MOVING)
+      || (moving_object->get_group() == COLGROUP_MOVING_STATIC)
+      || (moving_object->get_group() == COLGROUP_STATIC)) {
+      if(intersects(rect, moving_object->get_bbox())) return false;
+    }
+  }
+
+  return true;
+}
+
+bool
+Sector::add_bullet(const Vector& pos, float xm, Direction dir)
+{
+  // TODO remove this function and move these checks elsewhere...
+
+  Bullet* new_bullet = 0;
+  if((player_status->bonus == FIRE_BONUS &&
+      (int)bullets.size() >= player_status->max_fire_bullets) ||
+     (player_status->bonus == ICE_BONUS &&
+      (int)bullets.size() >= player_status->max_ice_bullets))
+    return false;
+  new_bullet = new Bullet(pos, xm, dir, player_status->bonus);
+  add_object(new_bullet);
+
+  sound_manager->play("sounds/shoot.wav");
+
+  return true;
+}
+
+bool
+Sector::add_smoke_cloud(const Vector& pos)
+{
+  add_object(new SmokeCloud(pos));
+  return true;
+}
+
+void
+Sector::play_music(MusicType type)
+{
+  currentmusic = type;
+  switch(currentmusic) {
+    case LEVEL_MUSIC:
+      sound_manager->play_music(music);
+      break;
+    case HERRING_MUSIC:
+      sound_manager->play_music("music/salcon.ogg");
+      break;
+    case HERRING_WARNING_MUSIC:
+      sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
+      break;
+    default:
+      sound_manager->play_music("");
+      break;
+  }
+}
+
+MusicType
+Sector::get_music_type()
+{
+  return currentmusic;
+}
+
+int
+Sector::get_total_badguys()
+{
+  int total_badguys = 0;
+  for(GameObjects::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i) {
+    BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
+    if (badguy && badguy->countMe)
+      total_badguys++;
+  }
+
+  return total_badguys;
+}
+
+bool
+Sector::inside(const Rect& rect) const
+{
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    bool horizontally = ((rect.p2.x >= 0 + solids->get_x_offset()) && (rect.p1.x <= solids->get_width() * 32 + solids->get_x_offset()));
+    bool vertically = (rect.p1.y <= solids->get_height() * 32 + solids->get_y_offset());
+
+    if (horizontally && vertically)
+      return true;
+  }
+  return false;
+}
+
+float
+Sector::get_width() const
+{
+  float width = 0;
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
+      i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    if ((solids->get_width() * 32 + solids->get_x_offset()) > width) {
+      width = solids->get_width() * 32 + solids->get_x_offset();
+    }
+  }
+
+  return width;
+}
+
+float
+Sector::get_height() const
+{
+  float height = 0;
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
+      i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    if ((solids->get_height() * 32 + solids->get_y_offset()) > height) {
+      height = solids->get_height() * 32 + solids->get_y_offset();
+    }
+  }
+
+  return height;
+}
+
+void
+Sector::change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id)
+{
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    solids->change_all(old_tile_id, new_tile_id);
+  }
+}
+
+
+void
+Sector::set_ambient_light(float red, float green, float blue)
+{
+  ambient_light.red = red;
+  ambient_light.green = green;
+  ambient_light.blue = blue;
+}
+
+float
+Sector::get_ambient_red()
+{
+  return ambient_light.red;
+}
+
+float
+Sector::get_ambient_green()
+{
+  return ambient_light.green;
+}
+
+float
+Sector::get_ambient_blue()
+{
+  return ambient_light.blue;
+}
diff --git a/src/sector.hpp b/src/sector.hpp
new file mode 100644 (file)
index 0000000..1fc487e
--- /dev/null
@@ -0,0 +1,275 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_SECTOR_H
+#define SUPERTUX_SECTOR_H
+
+#include <vector>
+#include <list>
+#include <memory>
+#include <squirrel.h>
+
+#include "direction.hpp"
+#include "video/color.hpp"
+#include "scripting/ssector.hpp"
+
+namespace lisp {
+class Lisp;
+class Writer;
+}
+namespace collision {
+class Constraints;
+}
+
+class Vector;
+class Rect;
+class Sprite;
+class GameObject;
+class Player;
+class Camera;
+class TileMap;
+class Bullet;
+class ScriptInterpreter;
+class SpawnPoint;
+class MovingObject;
+class CollisionHit;
+class Level;
+class Portable;
+class DrawingContext;
+
+enum MusicType {
+  LEVEL_MUSIC,
+  HERRING_MUSIC,
+  HERRING_WARNING_MUSIC
+};
+
+/**
+ * This class holds a sector (a part of a level) and all the game objects in
+ * the sector
+ */
+class Sector : public Scripting::SSector
+{
+public:
+  Sector(Level* parent);
+  ~Sector();
+
+  /// get parent level
+  Level* get_level();
+
+  /// read sector from lisp file
+  void parse(const lisp::Lisp& lisp);
+  void parse_old_format(const lisp::Lisp& lisp);
+  /// write sector to lisp file
+  void write(lisp::Writer& writer);
+
+  /// activates this sector (change music, intialize player class, ...)
+  void activate(const std::string& spawnpoint);
+  void activate(const Vector& player_pos);
+  void deactivate();
+
+  void update(float elapsed_time);
+  void update_game_objects();
+
+  void draw(DrawingContext& context);
+
+  /**
+   * runs a script in the context of the sector (sector_table will be the
+   * roottable of this squirrel VM)
+   */
+  HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+
+  /// adds a gameobject
+  void add_object(GameObject* object);
+
+  void set_name(const std::string& name)
+  { this->name = name; }
+  const std::string& get_name() const
+  { return name; }
+
+  /**
+   * tests if a given rectangle is inside the sector
+   * (a rectangle that is on top of the sector is considered inside)
+   */
+  bool inside(const Rect& rectangle) const;
+
+  void play_music(MusicType musictype);
+  MusicType get_music_type();
+
+  bool add_bullet(const Vector& pos, float xm, Direction dir);
+  bool add_smoke_cloud(const Vector& pos);
+
+  /** get currently activated sector. */
+  static Sector* current()
+  { return _current; }
+
+  /** Get total number of badguys */
+  int get_total_badguys();
+
+  /** Get total number of GameObjects of given type */
+  template<class T> int get_total_count()
+  {
+    int total = 0;
+    for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); ++i) {
+      if (dynamic_cast<T*>(*i)) total++;
+    }
+    return total;
+  }
+
+  void collision_tilemap(collision::Constraints* constraints,
+      const Vector& movement, const Rect& dest) const;
+
+  /**
+   * Checks if the specified rectangle is free of (solid) tiles.
+   * Note that this does not include static objects, e.g. bonus blocks.
+   */
+  bool is_free_of_tiles(const Rect& rect, const bool ignoreUnisolid = false) const;
+  /**
+   * Checks if the specified rectangle is free of both
+   * 1.) solid tiles and
+   * 2.) MovingObjects in COLGROUP_STATIC.
+   * Note that this does not include badguys or players.
+   */
+  bool is_free_of_statics(const Rect& rect, const MovingObject* ignore_object = 0, const bool ignoreUnisolid = false) const;
+  /**
+   * Checks if the specified rectangle is free of both
+   * 1.) solid tiles and
+   * 2.) MovingObjects in COLGROUP_STATIC, COLGROUP_MOVINGSTATIC or COLGROUP_MOVING.
+   * This includes badguys and players.
+   */
+  bool is_free_of_movingstatics(const Rect& rect, const MovingObject* ignore_object = 0) const;
+
+  /**
+   * returns a list of players currently in the sector
+   */
+  std::vector<Player*> get_players() {
+    return std::vector<Player*>(1, this->player);
+  }
+
+  Rect get_active_region();
+
+  /**
+   * returns the width (in px) of a sector)
+   */
+  float get_width() const;
+
+  /**
+   * returns the height (in px) of a sector)
+   */
+  float get_height() const;
+
+  /**
+   * globally changes solid tilemaps' tile ids
+   */
+  void change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id);
+
+  typedef std::vector<GameObject*> GameObjects;
+  typedef std::vector<MovingObject*> MovingObjects;
+  typedef std::vector<SpawnPoint*> SpawnPoints;
+  typedef std::vector<Portable*> Portables;
+
+  // --- Scripting ---
+  /**
+   *  get/set color of ambient light
+   */
+  void set_ambient_light(float red, float green, float blue);
+  float get_ambient_red();
+  float get_ambient_green();
+  float get_ambient_blue();
+
+private:
+  Level* level; /**< Parent level containing this sector */
+  uint32_t collision_tile_attributes(const Rect& dest) const;
+
+  void before_object_remove(GameObject* object);
+  bool before_object_add(GameObject* object);
+
+  void try_expose(GameObject* object);
+  void try_unexpose(GameObject* object);
+  void try_expose_me();
+  void try_unexpose_me();
+
+  /** Checks for all possible collisions. And calls the
+      collision_handlers, which the collision_objects provide for this
+      case (or not). */
+  void handle_collisions();
+
+  /**
+   * Does collision detection between 2 objects and does instant
+   * collision response handling in case of a collision
+   */
+  void collision_object(MovingObject* object1, MovingObject* object2) const;
+
+  /**
+   * Does collision detection of an object against all other static
+   * objects (and the tilemap) in the level. Collision response is done
+   * for the first hit in time. (other hits get ignored, the function
+   * should be called repeatedly to resolve those)
+   *
+   * returns true if the collision detection should be aborted for this object
+   * (because of ABORT_MOVE in the collision response or no collisions)
+   */
+  void collision_static(collision::Constraints* constraints,
+      const Vector& movement, const Rect& dest, GameObject& object);
+
+  void collision_static_constrains(MovingObject& object);
+
+  GameObject* parse_object(const std::string& name, const lisp::Lisp& lisp);
+
+  void fix_old_tiles();
+
+  static Sector* _current;
+
+  std::string name;
+
+  std::vector<Bullet*> bullets;
+
+  std::string init_script;
+
+  /// container for newly created objects, they'll be added in Sector::update
+  GameObjects gameobjects_new;
+
+  MusicType currentmusic;
+
+  HSQOBJECT sector_table;
+  /// sector scripts
+  typedef std::vector<HSQOBJECT> ScriptList;
+  ScriptList scripts;
+
+  Color ambient_light;
+
+public: // TODO make this private again
+  /// show collision rectangles of moving objects (for debugging)
+  static bool show_collrects;
+  static bool draw_solids_only;
+
+  GameObjects gameobjects;
+  MovingObjects moving_objects;
+  SpawnPoints spawnpoints;
+  Portables portables;
+
+  std::string music;
+  float gravity;
+
+  // some special objects, where we need direct access
+  // (try to avoid accessing them directly)
+  Player* player;
+  std::list<TileMap*> solid_tilemaps;
+  Camera* camera;
+};
+
+#endif
diff --git a/src/serializable.hpp b/src/serializable.hpp
new file mode 100644 (file)
index 0000000..74adedc
--- /dev/null
@@ -0,0 +1,33 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_SERIALIZABLE_H
+#define SUPERTUX_SERIALIZABLE_H
+
+namespace lisp { class Writer; }
+
+class Serializable
+{
+public:
+  virtual ~Serializable()
+  { }
+
+  virtual void write(lisp::Writer& writer) = 0;
+};
+
+#endif /*SUPERTUX_SERIALIZABLE_H*/
diff --git a/src/shrinkfade.cpp b/src/shrinkfade.cpp
new file mode 100644 (file)
index 0000000..d921efb
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "shrinkfade.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+
+ShrinkFade::ShrinkFade(const Vector& dest, float fade_time)
+  : dest(dest), fade_time(fade_time), accum_time(0)
+{
+  speedleft = dest.x / fade_time;
+  speedright = (SCREEN_WIDTH - dest.x) / fade_time;
+  speedtop = dest.y / fade_time;
+  speedbottom = (SCREEN_HEIGHT - dest.y) / fade_time;
+}
+
+ShrinkFade::~ShrinkFade()
+{
+}
+
+void
+ShrinkFade::update(float elapsed_time)
+{
+  accum_time += elapsed_time;
+  if(accum_time > fade_time)
+    accum_time = fade_time;
+}
+
+void
+ShrinkFade::draw(DrawingContext& context)
+{
+  Color black(0, 0, 0);
+  float left = speedleft * accum_time;
+  float top = speedtop * accum_time;
+  float right = SCREEN_WIDTH - speedright * accum_time;
+  float bottom = SCREEN_HEIGHT - speedbottom * accum_time;
+
+  context.draw_filled_rect(Vector(0, 0),
+                           Vector(left, SCREEN_HEIGHT),
+                           black, LAYER_GUI+1);
+  context.draw_filled_rect(Vector(0, 0),
+                           Vector(SCREEN_WIDTH, top),
+                           black, LAYER_GUI+1);
+  context.draw_filled_rect(Vector(right, 0),
+                           Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+                           black, LAYER_GUI+1);
+  context.draw_filled_rect(Vector(0, bottom),
+                           Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+                           black, LAYER_GUI+1);
+}
+
+bool
+ShrinkFade::done()
+{
+  return accum_time >= fade_time;
+}
diff --git a/src/shrinkfade.hpp b/src/shrinkfade.hpp
new file mode 100644 (file)
index 0000000..7afe642
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SHRINKFADE_HPP__
+#define __SHRINKFADE_HPP__
+
+#include "screen_fade.hpp"
+#include "math/vector.hpp"
+
+/**
+ * Shrinks a rectangle screen towards a specific position
+ */
+class ShrinkFade : public ScreenFade
+{
+public:
+  ShrinkFade(const Vector& point, float fade_time);
+  virtual ~ShrinkFade();
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+
+  virtual bool done();
+
+private:
+  Vector dest;
+  float fade_time;
+  float accum_time;
+  float speedleft, speedright, speedtop, speedbottom;
+};
+
+#endif
diff --git a/src/spawn_point.cpp b/src/spawn_point.cpp
new file mode 100644 (file)
index 0000000..8cfd13a
--- /dev/null
@@ -0,0 +1,58 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include <iostream>
+#include "spawn_point.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+SpawnPoint::SpawnPoint()
+{}
+
+SpawnPoint::SpawnPoint(const SpawnPoint& other)
+    : name(other.name), pos(other.pos)
+{}
+
+SpawnPoint::SpawnPoint(const lisp::Lisp* slisp)
+{
+    pos.x = -1;
+    pos.y = -1;
+    lisp::ListIterator iter(slisp);
+    while(iter.next()) {
+        const std::string& token = iter.item();
+        if(token == "name") {
+            iter.value()->get(name);
+        } else if(token == "x") {
+            iter.value()->get(pos.x);
+        } else if(token == "y") {
+            iter.value()->get(pos.y);
+        } else {
+            log_warning << "unknown token '" << token << "' in SpawnPoint" << std::endl;
+        }
+    }
+
+    if(name == "")
+        throw std::runtime_error("No name specified for spawnpoint");
+    if(pos.x < 0 || pos.y < 0)
+        throw std::runtime_error("Invalid coordinates for spawnpoint");
+}
diff --git a/src/spawn_point.hpp b/src/spawn_point.hpp
new file mode 100644 (file)
index 0000000..23bafa8
--- /dev/null
@@ -0,0 +1,37 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SPAWN_POINT_H__
+#define __SPAWN_POINT_H__
+
+#include <string>
+#include "math/vector.hpp"
+namespace lisp { class Lisp; }
+
+class SpawnPoint
+{
+public:
+    SpawnPoint();
+    SpawnPoint(const SpawnPoint& other);
+    SpawnPoint(const lisp::Lisp* lisp);
+
+    std::string name;
+    Vector pos;
+};
+
+#endif
diff --git a/src/sprite/sprite.cpp b/src/sprite/sprite.cpp
new file mode 100644 (file)
index 0000000..0841a06
--- /dev/null
@@ -0,0 +1,228 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+#include <cmath>
+#include <cassert>
+#include <stdexcept>
+
+
+#include "video/surface.hpp"
+#include "sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+Sprite::Sprite(SpriteData& newdata)
+  : data(newdata),
+    frame(0),
+    animation_loops(-1),
+    angle(0.0f),
+    color(1.0f, 1.0f, 1.0f, 1.0f)
+{
+  action = data.get_action("normal");
+  if(!action)
+    action = data.actions.begin()->second;
+  last_ticks = game_time;
+}
+
+Sprite::Sprite(const Sprite& other)
+  : data(other.data), frame(other.frame),
+    animation_loops(other.animation_loops),
+    angle(0.0f),
+    color(1.0f, 1.0f, 1.0f, 1.0f),
+    action(other.action)
+{
+  last_ticks = game_time;
+}
+
+Sprite::~Sprite()
+{
+}
+
+void
+Sprite::set_action(const std::string& name, int loops)
+{
+  if(action && action->name == name)
+    return;
+
+  SpriteData::Action* newaction = data.get_action(name);
+  if(!newaction) {
+    log_debug << "Action '" << name << "' not found." << std::endl;
+    return;
+  }
+
+  action = newaction;
+  animation_loops = loops;
+  frame = 0;
+}
+
+bool
+Sprite::animation_done()
+{
+  return animation_loops == 0;
+}
+
+void
+Sprite::update()
+{
+  if(animation_done())
+    return;
+
+  float frame_inc = action->fps * (game_time - last_ticks);
+  last_ticks = game_time;
+
+  frame += frame_inc;
+
+  if(frame >= get_frames()) {
+    frame = fmodf(frame, get_frames());
+
+    animation_loops--;
+    if(animation_done())
+      frame = get_frames()-1;
+  }
+}
+
+void
+Sprite::draw(DrawingContext& context, const Vector& pos, int layer)
+{
+  assert(action != 0);
+  update();
+
+  if((int)frame >= get_frames() || (int)frame < 0)
+    log_warning << "frame out of range: " << (int)frame << "/" << get_frames() << " at " << get_name() << "/" << get_action() << std::endl;
+  else
+    context.draw_surface(action->surfaces[(int)frame],
+                         pos - Vector(action->x_offset, action->y_offset),
+                         angle,
+                         color,
+                         blend,
+                         layer + action->z_order);
+}
+
+void
+Sprite::draw_part(DrawingContext& context, const Vector& source,
+    const Vector& size, const Vector& pos, int layer)
+{
+  assert(action != 0);
+  update();
+
+  int frameidx = (int) frame;
+
+  if(frameidx >= get_frames() || frameidx < 0) {
+#ifndef DEBUG
+    // in optimized mode we get some small rounding errors in floating point
+    // number sometimes...
+    log_warning << "frame out of range: " << frameidx << "/" << get_frames() << " at sprite: " << get_name() << "/" << get_action() << std::endl;
+#endif
+    frameidx = get_frames() - 1;
+  }
+
+  context.draw_surface_part(action->surfaces[frameidx], source, size,
+      pos - Vector(action->x_offset, action->y_offset),
+      layer + action->z_order);
+}
+
+int
+Sprite::get_width() const
+{
+  return (int) action->surfaces[get_frame()]->get_width();
+}
+
+int
+Sprite::get_height() const
+{
+  return (int) action->surfaces[get_frame()]->get_height();
+}
+
+float
+Sprite::get_current_hitbox_x_offset() const
+{
+  return action->x_offset;
+}
+
+float
+Sprite::get_current_hitbox_y_offset() const
+{
+  return action->y_offset;
+}
+
+float
+Sprite::get_current_hitbox_width() const
+{
+  return action->hitbox_w;
+}
+
+float
+Sprite::get_current_hitbox_height() const
+{
+  return action->hitbox_h;
+}
+
+Rect
+Sprite::get_current_hitbox() const
+{
+  return Rect(action->x_offset, action->y_offset, action->x_offset + action->hitbox_w, action->y_offset + action->hitbox_h);
+}
+
+void
+Sprite::set_fps(float new_fps)
+{
+  action->fps = new_fps;
+}
+
+void
+Sprite::set_angle(float a)
+{
+  angle = a;
+}
+
+float
+Sprite::get_angle() const
+{
+  return angle;
+}
+
+void
+Sprite::set_color(const Color& c)
+{
+  color = c;
+}
+
+Color
+Sprite::get_color() const
+{
+  return color;
+}
+
+void
+Sprite::set_blend(const Blend& b)
+{
+  blend = b;
+}
+
+Blend
+Sprite::get_blend() const
+{
+  return blend;
+}
+
+/* EOF */
diff --git a/src/sprite/sprite.hpp b/src/sprite/sprite.hpp
new file mode 100644 (file)
index 0000000..a3edb33
--- /dev/null
@@ -0,0 +1,134 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SPRITE_H
+#define SUPERTUX_SPRITE_H
+
+#include <string>
+#include <assert.h>
+
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "sprite_data.hpp"
+#include "video/color.hpp"
+#include "video/drawing_context.hpp"
+
+class Surface;
+class DrawingContext;
+class Blend;
+
+class Sprite
+{
+public:
+  Sprite(SpriteData& data);
+  Sprite(const Sprite& other);
+  ~Sprite();
+
+  /** Draw sprite, automatically calculates next frame */
+  void draw(DrawingContext& context, const Vector& pos, int layer);
+
+  void draw_part(DrawingContext& context, const Vector& source,
+      const Vector& size, const Vector& pos, int layer);
+
+  /** Set action (or state) */
+  void set_action(const std::string& act, int loops = -1);
+
+  /** Set number of animation cycles until animation stops */
+  void set_animation_loops(int loops = -1)
+  { animation_loops = loops; }
+
+  /** Set framerate */
+  void set_fps(float new_fps);
+
+  /* Stop animation */
+  void stop_animation()
+  { animation_loops = 0; }
+  /** Check if animation is stopped or not */
+  bool animation_done();
+
+  float get_fps() const
+  { return action->fps; }
+  /** Get current action total frames */
+  int get_frames() const
+  { return action->surfaces.size(); }
+  /** Get sprite's name */
+  const std::string& get_name() const
+  { return data.name; }
+  /** Get current action name */
+  const std::string& get_action() const
+  { return action->name; }
+
+  int get_width() const;
+  int get_height() const;
+
+  /** return x-offset of current action's hitbox, relative to start of image */
+  float get_current_hitbox_x_offset() const;
+  /** return y-offset of current action's hitbox, relative to start of image */
+  float get_current_hitbox_y_offset() const;
+  /** return width of current action's hitbox */
+  float get_current_hitbox_width() const;
+  /** return height of current action's hitbox */
+  float get_current_hitbox_height() const;
+  /** return current action's hitbox, relative to 0,0 */
+  Rect get_current_hitbox() const;
+
+  /** Set the angle of the sprite rotation in degree */
+  void set_angle(float angle);
+
+  /** Get the angle of the sprite rotation in degree */
+  float get_angle() const;
+
+  void set_color(const Color& color);
+
+  Color get_color() const;
+
+  void set_blend(const Blend& blend);
+
+  Blend get_blend() const;
+
+  /** Get current frame */
+  int get_frame() const
+  { return (int)frame; }
+  /** Set current frame */
+  void set_frame(int frame)
+  {
+    this->frame = (float) (frame % get_frames());
+  }
+  Surface* get_frame(unsigned int frame)
+  {
+    assert(frame < action->surfaces.size());
+    return action->surfaces[frame];
+  }
+
+private:
+  void update();
+
+  SpriteData& data;
+
+  float frame;
+  int   animation_loops;
+  float last_ticks;
+  float angle;
+  Color color;
+  Blend blend;
+
+  SpriteData::Action* action;
+};
+
+#endif
diff --git a/src/sprite/sprite_data.cpp b/src/sprite/sprite_data.cpp
new file mode 100644 (file)
index 0000000..52e404a
--- /dev/null
@@ -0,0 +1,145 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+#include <cmath>
+#include <sstream>
+#include <stdexcept>
+
+#include "sprite_data.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+SpriteData::Action::Action()
+{
+  x_offset = 0;
+  y_offset = 0;
+  hitbox_w = 0;
+  hitbox_h = 0;
+  z_order = 0;
+  fps = 10;
+}
+
+SpriteData::Action::~Action()
+{
+  for(std::vector<Surface*>::iterator i = surfaces.begin();
+      i != surfaces.end(); ++i)
+    delete *i;
+}
+
+SpriteData::SpriteData(const lisp::Lisp* lisp, const std::string& basedir)
+{
+  lisp::ListIterator iter(lisp);
+  while(iter.next()) {
+    if(iter.item() == "name") {
+      iter.value()->get(name);
+    } else if(iter.item() == "action") {
+      parse_action(iter.lisp(), basedir);
+    } else {
+      log_warning << "Unknown sprite field: " << iter.item() << std::endl;
+    }
+  }
+  if(actions.empty())
+    throw std::runtime_error("Error: Sprite wihtout actions.");
+}
+
+SpriteData::~SpriteData()
+{
+  for(Actions::iterator i=actions.begin(); i != actions.end(); ++i)
+    delete i->second;
+}
+
+void
+SpriteData::parse_action(const lisp::Lisp* lisp, const std::string& basedir)
+{
+  Action* action = new Action;
+
+  if(!lisp->get("name", action->name)) {
+    if(!actions.empty())
+      throw std::runtime_error(
+          "If there are more than one action, they need names!");
+  }
+  std::vector<float> hitbox;
+  if (lisp->get_vector("hitbox", hitbox)) {
+    if (hitbox.size() != 4) throw std::runtime_error("hitbox must specify exactly 4 coordinates");
+    action->x_offset = hitbox[0];
+    action->y_offset = hitbox[1];
+    action->hitbox_w = hitbox[2];
+    action->hitbox_h = hitbox[3];
+  }
+  lisp->get("z-order", action->z_order);
+  lisp->get("fps", action->fps);
+
+  std::string mirror_action;
+  lisp->get("mirror-action", mirror_action);
+  if(!mirror_action.empty()) {
+    Action* act_tmp = get_action(mirror_action);
+    if(act_tmp == NULL) {
+      throw std::runtime_error("Could not mirror action. Action not found\n"
+                   "Mirror actions must be defined after the real one!");
+    } else {
+      float max_w = 0;
+      float max_h = 0;
+      for(int i = 0; static_cast<unsigned int>(i) < act_tmp->surfaces.size();
+          i++) {
+        Surface* surface = new Surface(*(act_tmp->surfaces[i]));
+        surface->hflip();
+        max_w = std::max(max_w, (float) surface->get_width());
+        max_h = std::max(max_h, (float) surface->get_height());
+        action->surfaces.push_back(surface);
+      }
+      if (action->hitbox_w < 1) action->hitbox_w = max_w;
+      if (action->hitbox_h < 1) action->hitbox_h = max_h;
+    }
+  } else { // Load images
+    std::vector<std::string> images;
+    if(!lisp->get_vector("images", images)) {
+      std::stringstream msg;
+      msg << "Sprite '" << name << "' contains no images in action '"
+          << action->name << "'.";
+      throw std::runtime_error(msg.str());
+    }
+
+    float max_w = 0;
+    float max_h = 0;
+    for(std::vector<std::string>::size_type i = 0; i < images.size(); i++) {
+      Surface* surface = new Surface(basedir + images[i]);
+      max_w = std::max(max_w, (float) surface->get_width());
+      max_h = std::max(max_h, (float) surface->get_height());
+      action->surfaces.push_back(surface);
+    }
+    if (action->hitbox_w < 1) action->hitbox_w = max_w;
+    if (action->hitbox_h < 1) action->hitbox_h = max_h;
+  }
+  actions[action->name] = action;
+}
+
+SpriteData::Action*
+SpriteData::get_action(std::string act)
+{
+  Actions::iterator i = actions.find(act);
+  if(i == actions.end()) {
+    return 0;
+  }
+  return i->second;
+}
diff --git a/src/sprite/sprite_data.hpp b/src/sprite/sprite_data.hpp
new file mode 100644 (file)
index 0000000..bc5ccb3
--- /dev/null
@@ -0,0 +1,81 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SPRITE_DATA_H
+#define SUPERTUX_SPRITE_DATA_H
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "lisp/lisp.hpp"
+#include "video/surface.hpp"
+
+class SpriteData
+{
+public:
+  /** cur has to be a pointer to data in the form of ((hitbox 5 10 0 0) ...) */
+  SpriteData(const lisp::Lisp* cur, const std::string& basedir);
+  ~SpriteData();
+
+  const std::string& get_name() const
+  {
+    return name;
+  }
+
+private:
+  friend class Sprite;
+
+  struct Action
+  {
+    Action();
+    ~Action();
+
+    std::string name;
+
+    /** Position correction */
+    float x_offset;
+    float y_offset;
+
+    /** Hitbox width */
+    float hitbox_w;
+
+    /** Hitbox height */
+    float hitbox_h;
+
+    /** Drawing priority in queue */
+    int z_order;
+
+    /** Frames per second */
+    float fps;
+
+    std::vector<Surface*> surfaces;
+  };
+
+  typedef std::map <std::string, Action*> Actions;
+  Actions actions;
+
+  void parse_action(const lisp::Lisp* lispreader, const std::string& basedir);
+  /** Get an action */
+  Action* get_action(std::string act);
+
+  std::string name;
+};
+
+#endif
diff --git a/src/sprite/sprite_manager.cpp b/src/sprite/sprite_manager.cpp
new file mode 100644 (file)
index 0000000..f1de683
--- /dev/null
@@ -0,0 +1,95 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include "sprite_manager.hpp"
+#include "sprite_data.hpp"
+#include "sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/list_iterator.hpp"
+#include "file_system.hpp"
+#include "log.hpp"
+
+SpriteManager* sprite_manager = NULL;
+
+SpriteManager::SpriteManager()
+{
+}
+
+SpriteManager::~SpriteManager()
+{
+  for(Sprites::iterator i = sprites.begin(); i != sprites.end(); ++i) {
+    delete i->second;
+  }
+}
+
+Sprite*
+SpriteManager::create(const std::string& name)
+{
+  Sprites::iterator i = sprites.find(name);
+  SpriteData* data;
+  if(i == sprites.end()) {
+    // try loading the spritefile
+    data = load(name);
+    if(data == NULL) {
+      std::stringstream msg;
+      msg << "Sprite '" << name << "' not found.";
+      throw std::runtime_error(msg.str());
+    }
+  } else {
+    data = i->second;
+  }
+
+  return new Sprite(*data);
+}
+
+SpriteData*
+SpriteManager::load(const std::string& filename)
+{
+  lisp::Parser parser;
+  const lisp::Lisp* root;
+
+  try {
+    root = parser.parse(filename);
+  } catch(const std::exception& e) {
+    std::ostringstream msg;
+    msg << "Parse error when trying to load sprite '" << filename
+        << "': " << e.what() << "\n";
+    throw std::runtime_error(msg.str());
+  }
+
+  const lisp::Lisp* sprite = root->get_lisp("supertux-sprite");
+  if(!sprite) {
+    std::ostringstream msg;
+    msg << "'" << filename << "' is not a supertux-sprite file";
+    throw std::runtime_error(msg.str());
+  }
+
+  std::auto_ptr<SpriteData> data (
+      new SpriteData(sprite, FileSystem::dirname(filename)) );
+  sprites[filename] = data.release();
+
+  return sprites[filename];
+}
diff --git a/src/sprite/sprite_manager.hpp b/src/sprite/sprite_manager.hpp
new file mode 100644 (file)
index 0000000..7b5c1e4
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SPRITE_MANAGER_H
+#define SUPERTUX_SPRITE_MANAGER_H
+
+#include <map>
+
+class SpriteData;
+class Sprite;
+
+class SpriteManager
+{
+private:
+  typedef std::map<std::string, SpriteData*> Sprites;
+  Sprites sprites;
+
+public:
+  SpriteManager();
+  ~SpriteManager();
+
+  /** loads a sprite. */
+  Sprite* create(const std::string& filename);
+
+private:
+  SpriteData* load(const std::string& filename);
+};
+
+extern SpriteManager* sprite_manager;
+
+#endif
diff --git a/src/squirrel/CMakeLists.txt b/src/squirrel/CMakeLists.txt
new file mode 100644 (file)
index 0000000..766685b
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# SuperTux - squirrel library build script
+# Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+## Add include/ to include directories
+
+INCLUDE_DIRECTORIES(${SUPERTUX_SOURCE_DIR}/src/squirrel/include/)
+
+## build list of source files
+
+FILE(GLOB SQUIRREL_SOURCES squirrel/*.cpp sqstdlib/*.cpp sqstdlib/*.c)
+
+## add additional compiler switches
+
+ADD_DEFINITIONS(-include ${CMAKE_BINARY_DIR}/config.h)
+
+## define a target for building the library
+
+ADD_LIBRARY(squirrel ${SQUIRREL_SOURCES})
diff --git a/src/squirrel/COPYRIGHT b/src/squirrel/COPYRIGHT
new file mode 100644 (file)
index 0000000..31e565a
--- /dev/null
@@ -0,0 +1,29 @@
+Copyright (c) 2003-2007 Alberto Demichelis\r
+\r
+This software is provided 'as-is', without any \r
+express or implied warranty. In no event will the \r
+authors be held liable for any damages arising from \r
+the use of this software.\r
+\r
+Permission is granted to anyone to use this software \r
+for any purpose, including commercial applications, \r
+and to alter it and redistribute it freely, subject \r
+to the following restrictions:\r
+\r
+               1. The origin of this software must not be \r
+               misrepresented; you must not claim that \r
+               you wrote the original software. If you \r
+               use this software in a product, an \r
+               acknowledgment in the product \r
+               documentation would be appreciated but is \r
+               not required.\r
+\r
+               2. Altered source versions must be plainly \r
+               marked as such, and must not be \r
+               misrepresented as being the original \r
+               software.\r
+\r
+               3. This notice may not be removed or \r
+               altered from any source distribution.\r
+-----------------------------------------------------\r
+END OF COPYRIGHT
\ No newline at end of file
diff --git a/src/squirrel/HISTORY b/src/squirrel/HISTORY
new file mode 100644 (file)
index 0000000..a76d0ff
--- /dev/null
@@ -0,0 +1,304 @@
+***version 2.1.2 stable***\r
+-new behaviour for generators iteration using foreach\r
+now when a generator is iterated by foreach the value returned by a 'return val' statement\r
+will terminate the iteration but will not be returned as foreach iteration\r
+-added sq_setclassudsize()\r
+-added sq_clear()\r
+-added table.clear(), array.clear()\r
+-fixed sq_cmp() (thx jyuill)\r
+-fixed minor bugs\r
+\r
+***2006-08-21       ***\r
+***version 2.1.1 stable***\r
+-vm refactoring\r
+-optimized internal function memory layout\r
+-new global symbol _version_ (is the version string)\r
+-code size optimization for float literals(on 32bits float builts)\r
+-now the raw ref API(sq_addref etc...) is fully reentrant.\r
+-fixed a bug in sq_getdelegate() now pushes null if the object doesn't have a delegate(thx MatzeB)\r
+-improved C reference performances in NO_GARBAGE_COLLECTOR builds\r
+-sq_getlocal() now enumerates also outer values.\r
+-fixed regexp library for GCC users.\r
+\r
+***2006-03-19       ***\r
+***version 2.1 stable***\r
+-added static class fields, new keyword static\r
+-added 64bits architecture support\r
+-added global slot _intsize_ int the base lib to recognize 32bits and 64bits builds\r
+-added functions with fixed environment, closure.bindenv() built-in function\r
+-all types except userdata and null implement the tostring() method\r
+-string concatenation now invokes metamethod _tostring\r
+-new metamethods for class objects _newmember and _inherited\r
+-sq_call() sq_resume() sq_wakeupvm() have a new signature\r
+-new C referencing implementation(scales more with the amount of references)\r
+-refactored hash table\r
+-new api functions sq_newslot(),sq_tobool(),sq_getbase(), sq_instanceof(), sq_bindenv()\r
+-the api func sq_createslot was deprecated but still supported in form of C macro on top of sq_newslot\r
+-sq_setreleasehook() now also works for classes\r
+-stream.readstr() and stream.writestr() have been deprecated(this affects file and blob)\r
+-fixed squirrel.h undeclared api calls\r
+-fixed few minor bugs\r
+-SQChar is now defined as wchar_t\r
+-removed warning when building with -Wall -pedantic for GCC users\r
+-added new std io function writeclosuretofile()\r
+-added new std string functions strip(),rstrip(),lstrip() and split()\r
+-regular expressions operators (+,*) now have more POSIX greedyness behaviour\r
+-class constructors are now invoked as normal functions\r
+\r
+***2005-10-02         ***\r
+***version 2.0.5 stable***\r
+-fixed some 64bits incompatibilities (thx sarge)\r
+-fixed minor bug in the stdlib format() function (thx Rick)\r
+-fixed a bug in dofile() that was preventing to compile empty files\r
+-added new API sq_poptop() & sq_getfreevariable()\r
+-some performance improvements\r
+\r
+***2005-08-14         ***\r
+***version 2.0.4 stable***\r
+-weak references and related API calls\r
+-added sq_objtobool()\r
+-class instances memory policies improved(1 mem allocation for the whole instance)\r
+-typetags are now declared as SQUserPointer instead of unsigned int\r
+-first pass for 64bits compatibility\r
+-fixed minor bug in the stdio stream\r
+-fixed a bug in format()\r
+-fixed bug in string.tointeger() and string.tofloat()\r
+\r
+***2005-06-24         ***\r
+***version 2.0.3 stable***\r
+-dofile() and loadfile() in the iolib now can decode ASCII, UTF8 files UCS2 big-endian and little-endian\r
+-sq_setparamscheck() : now typemesk can check for null\r
+-added string escape sequence \xhhhh\r
+-fixed some C++ standard incompatibilities\r
+\r
+***2005-05-15         ***\r
+***version 2.0.2 stable***\r
+-performances improvements (expecially for GCC users)\r
+-removed all dependencies from C++ exception handling\r
+-various bugfixes\r
+\r
+***2005-04-12           ***\r
+***version 2.0.1 stable***\r
+-various bugfixes\r
+-sq_setparamscheck() now allows spaces in the typemask\r
+\r
+***2005-04-03           ***\r
+***version 2.0 stable***\r
+-added API sq_gettypetag()\r
+-added built-in function to the bool type(tointeger, tostring etc...)\r
+\r
+***2005-02-27                                                  ***\r
+***version 2.0 release candidate 1(RC 1)***\r
+-added API sq_reseterror()\r
+-modified sq_release()\r
+-now class instances can be cloned\r
+-various bufixes\r
+\r
+***2005-01-26        ***\r
+***version 2.0 beta 1***\r
+-added bool type\r
+-class properties can be redefined in a derived class\r
+-added ops *= /= and %=\r
+-new syntax for class attributes declaration </ and /> instead of ( and )\r
+-increased the max number of literals per function from 65535 to 16777215\r
+-now free variables have proper lexical scoping\r
+-added API sq_createinstance(), sq_pushbool(), sq_getbool()\r
+-added built-in function type()\r
+-added built-in function obj.rawin(key) in table,class and instance\r
+-sq_rawget() and sq_rawset() now work also on classes and instances\r
+-the VM no longer uses C++ exception handling (more suitable for embedded devices)\r
+-various bufixes\r
+\r
+***2004-12-21         ***\r
+***version 2.0 alpha 2***\r
+-globals scoping changed, now if :: is omitted the VM automatically falls back on the root table\r
+-various bufixes\r
+-added class level attributes\r
+\r
+***2004-12-12         ***\r
+***version 2.0 alpha 1***\r
+-codebase branch from version 1.x\r
+-added classes\r
+-added functions with variable number of parameters(vargc & vargv and the ...)\r
+-0 and 0.0 are now considered 'false' by all conditional statements(if,while,for,?,do-while)\r
+-added new api functions sq_newclass() sq_setinstanceup() sq_getinstanceup() sq_getattributes() sq_setattributes()\r
+-modified api sq_settypetag()\r
+\r
+***2004-11-01        ***\r
+***version 1.0 stable***\r
+-fixed some minor bug\r
+-improoved operator 'delete' performances\r
+-added scientific notation for float numbers( eg. 2.e16 or 2.e-2)\r
+\r
+***2004-08-30        ***\r
+***version 1.0 release candidate 2(RC 2)***\r
+-fixed bug in the vm(thx Pierre Renaux)\r
+-fixed bug in the optimizer(thx Pierre Renaux)\r
+-fixed some bug in the documentation(thx JD)\r
+-added new api functions for raw object handling\r
+-removed nested multiline comments\r
+-reduced memory footprint in C references\r
+\r
+***2004-08-23        ***\r
+***version 1.0 release candidate 1(RC 1)***\r
+-fixed division by zero\r
+-the 'in' operator and obj.rawget() do not query the default delegate anymore\r
+-added function sq_getprintfunc()\r
+-added new standard library 'auxlib'(implements default error handlers)\r
+\r
+***2004-07-12        ***\r
+***version 1.0 beta 4***\r
+-fixed a bug in the integer.tochar() built-in method\r
+-fixed unary minus operator\r
+-fixed bug in dofile()\r
+-fixed inconsistency between != and == operators(on float/integer comparison)\r
+-added javascript style unsigned right shift operator '>>>'\r
+-added array(size) constructor built-in function\r
+-array.resize(size,[fill]) built-in function accepts an optional 'fill' value\r
+-improved debug API, added sq_getclosureinfo() and sq_setnativeclosurename()\r
+\r
+***2004-05-23        ***\r
+***version 1.0 beta 3***\r
+-minor vm bug fixes\r
+-string allocation is now faster\r
+-tables and array memory usage is now less conservative(they shrink)\r
+-added regular expression routines in the standard library\r
+-The 'c' expression now accepts only 1 character(thx irbrian)\r
+-multiline strings <[ ]> have been substituted with C# style verbatim strings (eg. @"string")\r
+-added new keyword 'parent' for accessing the delegate of tables and unserdata\r
+-The metamethod '_clone' has been renamed '_cloned'\r
+-the _delslot metamethod's behaviour and prototype have been changed\r
+-new default function in the integer and float object 'tochar()'\r
+-the built-in function chcode2string has been removed\r
+-the default method [table].getdelegate() has been removed\r
+-new api sq_rawdeleteslot()\r
+-new table built-in method rawdelete(key)\r
+-the dynamic mudule loading has been removed from the standard distribution\r
+-some optimizations in the VM\r
+\r
+***2004-04-21        ***\r
+***version 1.0 beta 2***\r
+-minor compiler/parser bug fixes\r
+-sq_newclosure has a different prototype, the "paramscheck" of paramter has been moved to the new function sq_setparamscheck()\r
+-sq_setparamscheck allows to add automatic parameters type checking in native closures\r
+-sq_compile() lost the lineinfo parameter\r
+-new api sq_enabledebuginfo() globally sets compiler's debug info generation\r
+-added consistency check on bytecode serialization\r
+-fixed += operator, now works on strings like +\r
+-added global slot in the base lib _charsize_ to recognize unicode builds from ascii builds runtime\r
+-added registry table\r
+-new api call sq_pushregistrytable()\r
+-added type tag to the userdata type sq_settypetag()\r
+-sq_getuserdata now queries the userdata typetag\r
+-the built in function collect_garbage() as been renamed collectgarbage() for consistency reasons\r
+-new standard libraries(sqlibs are now obsolete)\r
+\r
+***2004-02-20       ***\r
+***version 1.0 beta 1***\r
+-fixed a bug in the compiler (thanks Martin Kofler)\r
+-fixed bug in the switch case statement\r
+-fixed the _unm metamethod\r
+-fixed minor bugs in the API\r
+-fixed automatic stack resizing\r
+-first beta version \r
+       first pass code clean up in the VM and base lib\r
+       first pass code coverege test has been done on VM and built-in lib\r
+-new VM creation API sq_open() sq_close() (sq_newvm and sq_releasevm are now obsolete)\r
+-new api allows to specifiy a "print" function to output text(sq_printfunc)\r
+-added some small optimizations\r
+-new cooperative multi-threading capabilities in the base library(coroutines), VMs are now a built in type("thread")\r
+-new built in functions have been added for manipulating the new "thread" type\r
+-friend virtual machines share the same root table, error handler and debug hook by default\r
+-new compile time options\r
+\r
+***2004-01-19       ***\r
+***version 0.9 alpha***\r
+-fixed a garbage collection bug\r
+-fixed some API bugs(thanks to Joshua Jensen)\r
+-fixed tail calls (in the version 0.8 the tail call optimization was erroneously disabled)\r
+-new function parameters semantic, now passing a wrong number of parameters generates an exception\r
+-native closures have now a built in parameter number checking\r
+-sq_rawget and sq_rawset now work also on arrays\r
+-sq_getsize now woks also on userdata\r
+-the userdata release hook prototype is changed(now passes the size of the userdata)\r
+-the lexer reader function now returns an integer instead of a char that allows better error checking on the input(thx Joshua Jensen)\r
+-faster compiler\r
+-try/catch blocks do not cause any runtime memory allocation anymore\r
+\r
+***2003-12-06       ***\r
+***version 0.8 alpha***\r
+-fixed a bug that was preventing to have callable userdata throught the metamethod _call\r
+-fixed a garbage collection bug\r
+-fixed == operator now can compare correctly different types\r
+-new built in method getstackinfos(level)\r
+-improoved line informations precision for the debug hook\r
+-new api call sq_compilebuffer()\r
+-new built-in api function compilestring()\r
+-new syntactic sugar for function declarations inside tables\r
+-the debug API has been finalized\r
+\r
+***2003-11-17       ***\r
+***version 0.7 alpha***\r
+-fixed critical bug SQInteger the tail call system\r
+-fixed bug in the continue statement code generation\r
+-fixed func call param issue(thanks to Rewoonenco Andrew)\r
+-added _delslot metamethod(thanks to Rewoonenco Andrew)\r
+-new multiline string expression ( delimited by <[ and ]> )\r
+-normal strings ("") do not allow embedded new line anymore\r
+-reduced vm memory footprint(C refs are shared between friend VMs)\r
+-new api method sq_deleteslot()\r
+-new debug hook event 'r' is triggered when a function returns\r
+\r
+***2003-11-04       ***\r
+***version 0.6 alpha***\r
+-fixed switch statement(was executing the default case after a break)\r
+-sq_call() doesn't pop the closure (just the params)\r
+-the vm execution can be suspended from the C API anytime (micro-threads)\r
+-new api calls sq_suspendvm() sq_wakeupvm() sq_getvmstate() and sq_reservestack()\r
+\r
+***2003-10-13       ***\r
+***version 0.5 alpha***\r
+-fixed some minor bug\r
+-tested with non ASCII identifiers in unicode mode(I've tried chinese chars)\r
+-added built-in function string.find()\r
+-the built-in function array.sort() optionally accepts a cmp(a,b) function\r
+-the debug hook function now has a new prototype debug_hook(event_type,sourcefile,line,functionname)\r
+-fixed some debug info imprecision\r
+\r
+***2003-10-01       ***\r
+***version 0.4 alpha***\r
+-faster VM\r
+-sq_call will pop arguments and closure also in case of failure\r
+-fixed a bug in sq_remove\r
+-now the VM detects delegation cycles(and throws an exception)\r
+-new operators ++ and --\r
+-new operator ',' comma operator\r
+-fixed some expression precedence issue\r
+-fixed bug in sq_arraypop\r
+\r
+***2003-09-15       ***\r
+***version 0.3 alpha***\r
+-fixed a bug in array::insert()\r
+-optional Unicode core(define SQUNICODE or _UNICODE on Win32)\r
+-sq_compiler uses a new reader function SQLEXREADFUNC\r
+-the debug hook passes 'l' instead of 'line' for line callbacks\r
+       and 'c' instead of 'call' for call callbacks\r
+-new array.extend() bulit-in function\r
+-new API sq_clone()\r
+\r
+***2003-09-10           ***\r
+***version 0.2 pre-alpha***\r
+-new completely reentrant VM (sq_open and sq_close are now obsolete)\r
+-sq_newvm() has a new prototype\r
+-allocators are now global and linked in the VM\r
+-_newslot meta method added\r
+-rawset creates a slot if doesn't exists\r
+-the compiler error callback pass the vm handle(thanks Pierre Renaux)\r
+-sq_setforeignptr() sq_getforeingptr() are now public\r
+-sq_resume() now is possible to resume generators from C\r
+-sq_getlasterror() retrieve the last thrown error\r
+-improved docs\r
+\r
+***2003-09-06           ***\r
+***version 0.1 pre-alpha***\r
+first release\r
diff --git a/src/squirrel/Jamfile b/src/squirrel/Jamfile
new file mode 100644 (file)
index 0000000..c89d20f
--- /dev/null
@@ -0,0 +1,22 @@
+SubDir TOP src squirrel ;
+
+SQDBG_SOURCES = [ Wildcard sqdbg : *.cpp *.h *.inl ] ;
+if $(enable_sqdbg) = "yes" {
+    EXTRA_SOURCES = $(SQDBG_SOURCES) ;
+} else {
+    Package $(SQDBG_SOURCES) ;
+}
+
+Library squirrel
+    : [ Wildcard squirrel : *.cpp *.h ]
+      [ Wildcard sqstdlib : *.cpp *.c *.h ]
+      $(EXTRA_SOURCES)
+    : noinstall
+;
+
+for i in $(squirrel_OBJECTS) {
+    CXXFLAGS on $(i) = [ Filter [ on $(i) GetVar CXXFLAGS ] : -Wall -W -Werror ] -include $(top_builddir)/config.h ;
+    CFLAGS on $(i) = [ Filter [ on $(i) GetVar CFLAGS ] : -Wall -W -Werror ] -include $(top_builddir)/config.h ;
+}
+IncludeDir squirrel : include ;
+Package [ Wildcard include : *.h ] ;
diff --git a/src/squirrel/README b/src/squirrel/README
new file mode 100644 (file)
index 0000000..3d984d9
--- /dev/null
@@ -0,0 +1,22 @@
+The programming language SQUIRREL 2.1.2 stable\r
+\r
+--------------------------------------------------\r
+The project has been compiled and run on Windows(Windows XP/2000 on Intel x86 Windows XP Pro on AMD x64) and\r
+Linux(Slackware 9.0 on Intel x86, Fedora Core 4 on AMD x64).\r
+\r
+Has been tested with the following compilers:\r
+       MS Visual C++ 6.0,7.0,7.1 and 8.0 (32 and 64bits)\r
+       MinGW gcc 3.2 (mingw special 20020817-1)\r
+       Cygnus gcc 3.2\r
+       Linux gcc 3.2.3\r
+       Linux gcc 4.0.0 (x86 64bits)\r
+       \r
+\r
+Feedback and suggestions are appreciated \r
+project page - http://www.squirrel-lang.org\r
+community forums - http://www.squirrel-lang.org/Forums\r
+wiki - http://wiki.squirrel-lang.org\r
+author - alberto@ademichelis.com\r
+\r
+END OF README\r
+\r
diff --git a/src/squirrel/include/sqstdaux.h b/src/squirrel/include/sqstdaux.h
new file mode 100644 (file)
index 0000000..c16b043
--- /dev/null
@@ -0,0 +1,16 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_AUXLIB_H_
+#define _SQSTD_AUXLIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API void sqstd_seterrorhandlers(HSQUIRRELVM v);
+SQUIRREL_API void sqstd_printcallstack(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* _SQSTD_AUXLIB_H_ */
diff --git a/src/squirrel/include/sqstdblob.h b/src/squirrel/include/sqstdblob.h
new file mode 100644 (file)
index 0000000..1d9a4cd
--- /dev/null
@@ -0,0 +1,20 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTDBLOB_H_
+#define _SQSTDBLOB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size);
+SQUIRREL_API SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr);
+SQUIRREL_API SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx);
+
+SQUIRREL_API SQRESULT sqstd_register_bloblib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTDBLOB_H_*/
+
diff --git a/src/squirrel/include/sqstdio.h b/src/squirrel/include/sqstdio.h
new file mode 100644 (file)
index 0000000..6da1872
--- /dev/null
@@ -0,0 +1,57 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTDIO_H_
+#define _SQSTDIO_H_
+
+#ifdef __cplusplus
+
+#define SQSTD_STREAM_TYPE_TAG 0x80000000
+
+struct SQStream {
+
+       // [SuperTux] Added virtual destructor to avoid compiler warnings
+       virtual ~SQStream() { };
+
+       virtual SQInteger Read(void *buffer, SQInteger size) = 0;
+       virtual SQInteger Write(void *buffer, SQInteger size) = 0;
+       virtual SQInteger Flush() = 0;
+       virtual SQInteger Tell() = 0;
+       virtual SQInteger Len() = 0;
+       virtual SQInteger Seek(SQInteger offset, SQInteger origin) = 0;
+       virtual bool IsValid() = 0;
+       virtual bool EOS() = 0;
+};
+
+extern "C" {
+#endif
+
+#define SQ_SEEK_CUR 0
+#define SQ_SEEK_END 1
+#define SQ_SEEK_SET 2
+
+typedef void* SQFILE;
+
+SQUIRREL_API SQFILE sqstd_fopen(const SQChar *,const SQChar *);
+SQUIRREL_API SQInteger sqstd_fread(SQUserPointer, SQInteger, SQInteger, SQFILE);
+SQUIRREL_API SQInteger sqstd_fwrite(const SQUserPointer, SQInteger, SQInteger, SQFILE);
+SQUIRREL_API SQInteger sqstd_fseek(SQFILE , SQInteger , SQInteger);
+SQUIRREL_API SQInteger sqstd_ftell(SQFILE);
+SQUIRREL_API SQInteger sqstd_fflush(SQFILE);
+SQUIRREL_API SQInteger sqstd_fclose(SQFILE);
+SQUIRREL_API SQInteger sqstd_feof(SQFILE);
+
+SQUIRREL_API SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own);
+SQUIRREL_API SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file);
+
+//compiler helpers
+SQUIRREL_API SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror);
+SQUIRREL_API SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror);
+SQUIRREL_API SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename);
+
+SQUIRREL_API SQRESULT sqstd_register_iolib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTDIO_H_*/
+
diff --git a/src/squirrel/include/sqstdmath.h b/src/squirrel/include/sqstdmath.h
new file mode 100644 (file)
index 0000000..65de6fd
--- /dev/null
@@ -0,0 +1,15 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_MATH_H_
+#define _SQSTD_MATH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQRESULT sqstd_register_mathlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTD_MATH_H_*/
diff --git a/src/squirrel/include/sqstdstring.h b/src/squirrel/include/sqstdstring.h
new file mode 100644 (file)
index 0000000..72f30b4
--- /dev/null
@@ -0,0 +1,31 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_STRING_H_
+#define _SQSTD_STRING_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned int SQRexBool;
+typedef struct SQRex SQRex;
+
+typedef struct {
+       const SQChar *begin;
+       SQInteger len;
+} SQRexMatch;
+
+SQUIRREL_API SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error);
+SQUIRREL_API void sqstd_rex_free(SQRex *exp);
+SQUIRREL_API SQBool sqstd_rex_match(SQRex* exp,const SQChar* text);
+SQUIRREL_API SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end);
+SQUIRREL_API SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end);
+SQUIRREL_API SQInteger sqstd_rex_getsubexpcount(SQRex* exp);
+SQUIRREL_API SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp);
+
+SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTD_STRING_H_*/
diff --git a/src/squirrel/include/sqstdsystem.h b/src/squirrel/include/sqstdsystem.h
new file mode 100644 (file)
index 0000000..b155a91
--- /dev/null
@@ -0,0 +1,15 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_SYSTEMLIB_H_
+#define _SQSTD_SYSTEMLIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQInteger sqstd_register_systemlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* _SQSTD_SYSTEMLIB_H_ */
diff --git a/src/squirrel/include/squirrel.h b/src/squirrel/include/squirrel.h
new file mode 100644 (file)
index 0000000..5150312
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+Copyright (c) 2003-2007 Alberto Demichelis
+
+This software is provided 'as-is', without any 
+express or implied warranty. In no event will the 
+authors be held liable for any damages arising from 
+the use of this software.
+
+Permission is granted to anyone to use this software 
+for any purpose, including commercial applications, 
+and to alter it and redistribute it freely, subject 
+to the following restrictions:
+
+               1. The origin of this software must not be 
+               misrepresented; you must not claim that 
+               you wrote the original software. If you 
+               use this software in a product, an 
+               acknowledgment in the product 
+               documentation would be appreciated but is 
+               not required.
+
+               2. Altered source versions must be plainly 
+               marked as such, and must not be 
+               misrepresented as being the original 
+               software.
+
+               3. This notice may not be removed or 
+               altered from any source distribution.
+
+*/
+#ifndef _SQUIRREL_H_
+#define _SQUIRREL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef SQUIRREL_API
+#define SQUIRREL_API extern
+#endif
+
+#ifdef _SQ64
+#ifdef _MSC_VER
+typedef __int64 SQInteger;
+typedef unsigned __int64 SQUnsignedInteger;
+typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/
+#else
+typedef long SQInteger;
+typedef unsigned long SQUnsignedInteger;
+typedef unsigned long SQHash; /*should be the same size of a pointer*/
+#endif
+typedef int SQInt32; 
+#else 
+typedef int SQInteger;
+typedef int SQInt32; /*must be 32 bits(also on 64bits processors)*/
+typedef unsigned int SQUnsignedInteger;
+typedef unsigned int SQHash; /*should be the same size of a pointer*/
+#endif
+
+typedef float SQFloat;
+typedef void* SQUserPointer;
+typedef SQUnsignedInteger SQBool;
+typedef SQInteger SQRESULT;
+
+#define SQTrue (1)
+#define SQFalse        (0)
+
+
+struct SQVM;
+struct SQTable;
+struct SQArray;
+struct SQString;
+struct SQClosure;
+struct SQGenerator;
+struct SQNativeClosure;
+struct SQUserData;
+struct SQFunctionProto;
+struct SQRefCounted;
+struct SQClass;
+struct SQInstance;
+struct SQDelegable;
+
+#ifdef _UNICODE
+#define SQUNICODE
+#endif
+
+#ifdef SQUNICODE
+#if (defined(_MSC_VER) && _MSC_VER >= 1400) // 1400 = VS8
+
+#if defined(wchar_t) //this is if the compiler considers wchar_t as native type
+#define wchar_t unsigned short
+#endif
+
+#else
+typedef unsigned short wchar_t;
+#endif
+
+typedef wchar_t SQChar;
+#define _SC(a) L##a
+#define        scstrcmp        wcscmp
+#define scsprintf      swprintf
+#define scstrlen       wcslen
+#define scstrtod       wcstod
+#define scstrtol       wcstol
+#define scatoi         _wtoi
+#define scstrtoul      wcstoul
+#define scvsprintf     vswprintf
+#define scstrstr       wcsstr
+#define scisspace      iswspace
+#define scisdigit      iswdigit
+#define scisxdigit     iswxdigit
+#define scisalpha      iswalpha
+#define sciscntrl      iswcntrl
+#define scisalnum      iswalnum
+#define scprintf       wprintf
+#define MAX_CHAR 0xFFFF
+#else
+typedef char SQChar;
+#define _SC(a) a
+#define        scstrcmp        strcmp
+#define scsprintf      sprintf
+#define scstrlen       strlen
+#define scstrtod       strtod
+#define scstrtol       strtol
+#define scatoi         atoi
+#define scstrtoul      strtoul
+#define scvsprintf     vsprintf
+#define scstrstr       strstr
+#define scisspace      isspace
+#define scisdigit      isdigit
+#define scisxdigit     isxdigit
+#define sciscntrl      iscntrl
+#define scisalpha      isalpha
+#define scisalnum      isalnum
+#define scprintf       printf
+#define MAX_CHAR 0xFF
+#endif
+
+#define SQUIRREL_VERSION       _SC("Squirrel 2.1.2 stable")
+#define SQUIRREL_COPYRIGHT     _SC("Copyright (C) 2003-2007 Alberto Demichelis")
+#define SQUIRREL_AUTHOR                _SC("Alberto Demichelis")
+
+#define SQ_VMSTATE_IDLE                        0
+#define SQ_VMSTATE_RUNNING             1
+#define SQ_VMSTATE_SUSPENDED   2
+
+#define SQUIRREL_EOB 0
+#define SQ_BYTECODE_STREAM_TAG 0xFAFA
+
+#define SQOBJECT_REF_COUNTED   0x08000000
+#define SQOBJECT_NUMERIC               0x04000000
+#define SQOBJECT_DELEGABLE             0x02000000
+#define SQOBJECT_CANBEFALSE            0x01000000
+
+#define SQ_MATCHTYPEMASKSTRING (-99999)
+
+#define _RT_MASK 0x00FFFFFF
+#define _RAW_TYPE(type) (type&_RT_MASK)
+
+#define _RT_NULL                       0x00000001
+#define _RT_INTEGER                    0x00000002
+#define _RT_FLOAT                      0x00000004
+#define _RT_BOOL                       0x00000008
+#define _RT_STRING                     0x00000010
+#define _RT_TABLE                      0x00000020
+#define _RT_ARRAY                      0x00000040
+#define _RT_USERDATA           0x00000080
+#define _RT_CLOSURE                    0x00000100
+#define _RT_NATIVECLOSURE      0x00000200
+#define _RT_GENERATOR          0x00000400
+#define _RT_USERPOINTER                0x00000800
+#define _RT_THREAD                     0x00001000
+#define _RT_FUNCPROTO          0x00002000
+#define _RT_CLASS                      0x00004000
+#define _RT_INSTANCE           0x00008000
+#define _RT_WEAKREF                    0x00010000
+
+typedef enum tagSQObjectType{
+       OT_NULL =                       (_RT_NULL|SQOBJECT_CANBEFALSE),
+       OT_INTEGER =            (_RT_INTEGER|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
+       OT_FLOAT =                      (_RT_FLOAT|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
+       OT_BOOL =                       (_RT_BOOL|SQOBJECT_CANBEFALSE),
+       OT_STRING =                     (_RT_STRING|SQOBJECT_REF_COUNTED),
+       OT_TABLE =                      (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+       OT_ARRAY =                      (_RT_ARRAY|SQOBJECT_REF_COUNTED),
+       OT_USERDATA =           (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+       OT_CLOSURE =            (_RT_CLOSURE|SQOBJECT_REF_COUNTED),
+       OT_NATIVECLOSURE =      (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED),
+       OT_GENERATOR =          (_RT_GENERATOR|SQOBJECT_REF_COUNTED),
+       OT_USERPOINTER =        _RT_USERPOINTER,
+       OT_THREAD =                     (_RT_THREAD|SQOBJECT_REF_COUNTED) ,
+       OT_FUNCPROTO =          (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED), //internal usage only
+       OT_CLASS =                      (_RT_CLASS|SQOBJECT_REF_COUNTED),
+       OT_INSTANCE =           (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+       OT_WEAKREF =            (_RT_WEAKREF|SQOBJECT_REF_COUNTED)
+}SQObjectType;
+
+#define ISREFCOUNTED(t) (t&SQOBJECT_REF_COUNTED)
+
+
+typedef union tagSQObjectValue
+{
+       struct SQTable *pTable;
+       struct SQArray *pArray;
+       struct SQClosure *pClosure;
+       struct SQGenerator *pGenerator;
+       struct SQNativeClosure *pNativeClosure;
+       struct SQString *pString;
+       struct SQUserData *pUserData;
+       SQInteger nInteger;
+       SQFloat fFloat;
+       SQUserPointer pUserPointer;
+       struct SQFunctionProto *pFunctionProto;
+       struct SQRefCounted *pRefCounted;
+       struct SQDelegable *pDelegable;
+       struct SQVM *pThread;
+       struct SQClass *pClass;
+       struct SQInstance *pInstance;
+       struct SQWeakRef *pWeakRef;
+}SQObjectValue;
+
+
+typedef struct tagSQObject
+{
+       SQObjectType _type;
+       SQObjectValue _unVal;
+}SQObject;
+
+typedef struct tagSQStackInfos{
+       const SQChar* funcname;
+       const SQChar* source;
+       SQInteger line;
+}SQStackInfos;
+
+typedef struct SQVM* HSQUIRRELVM;
+typedef SQObject HSQOBJECT;
+typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM);
+typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size);
+typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/);
+typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const SQChar * ,...);
+
+typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger);
+typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger);
+
+typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer);
+
+typedef struct tagSQRegFunction{
+       const SQChar *name;
+       SQFUNCTION f;
+       SQInteger nparamscheck;
+       const SQChar *typemask;
+}SQRegFunction;
+
+/*vm*/
+SQUIRREL_API HSQUIRRELVM sq_open(SQInteger initialstacksize);
+SQUIRREL_API HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize);
+SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v);
+SQUIRREL_API void sq_close(HSQUIRRELVM v);
+SQUIRREL_API void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p);
+SQUIRREL_API SQUserPointer sq_getforeignptr(HSQUIRRELVM v);
+SQUIRREL_API void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc);
+SQUIRREL_API SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_suspendvm(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool resumedret,SQBool retval,SQBool raiseerror);
+SQUIRREL_API SQInteger sq_getvmstate(HSQUIRRELVM v);
+
+/*compiler*/
+SQUIRREL_API SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror);
+SQUIRREL_API SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror);
+SQUIRREL_API void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable);
+SQUIRREL_API void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable);
+SQUIRREL_API void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f);
+
+/*stack operations*/
+SQUIRREL_API void sq_push(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop);
+SQUIRREL_API void sq_poptop(HSQUIRRELVM v);
+SQUIRREL_API void sq_remove(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQInteger sq_gettop(HSQUIRRELVM v);
+SQUIRREL_API void sq_settop(HSQUIRRELVM v,SQInteger newtop);
+SQUIRREL_API void sq_reservestack(HSQUIRRELVM v,SQInteger nsize);
+SQUIRREL_API SQInteger sq_cmp(HSQUIRRELVM v);
+SQUIRREL_API void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx);
+
+/*object creation handling*/
+SQUIRREL_API SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size);
+SQUIRREL_API void sq_newtable(HSQUIRRELVM v);
+SQUIRREL_API void sq_newarray(HSQUIRRELVM v,SQInteger size);
+SQUIRREL_API void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars);
+SQUIRREL_API SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask);
+SQUIRREL_API SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len);
+SQUIRREL_API void sq_pushfloat(HSQUIRRELVM v,SQFloat f);
+SQUIRREL_API void sq_pushinteger(HSQUIRRELVM v,SQInteger n);
+SQUIRREL_API void sq_pushbool(HSQUIRRELVM v,SQBool b);
+SQUIRREL_API void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p);
+SQUIRREL_API void sq_pushnull(HSQUIRRELVM v);
+SQUIRREL_API SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQBool sq_instanceof(HSQUIRRELVM v);
+SQUIRREL_API void sq_tostring(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b);
+SQUIRREL_API SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c);
+SQUIRREL_API SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i);
+SQUIRREL_API SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f);
+SQUIRREL_API SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b);
+SQUIRREL_API SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread);
+SQUIRREL_API SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p);
+SQUIRREL_API SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag);
+SQUIRREL_API SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag);
+SQUIRREL_API SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag);
+SQUIRREL_API void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook);
+SQUIRREL_API SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize);
+SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars);
+SQUIRREL_API SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name);
+SQUIRREL_API SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p);
+SQUIRREL_API SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag);
+SQUIRREL_API SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase);
+SQUIRREL_API SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_weakref(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t);
+
+/*object manipulation*/
+SQUIRREL_API void sq_pushroottable(HSQUIRRELVM v);
+SQUIRREL_API void sq_pushregistrytable(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_setroottable(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic);
+SQUIRREL_API SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
+SQUIRREL_API SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
+SQUIRREL_API SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval); 
+SQUIRREL_API SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize); 
+SQUIRREL_API SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx); 
+SQUIRREL_API SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
+SQUIRREL_API SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
+
+/*calls*/
+SQUIRREL_API SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror);
+SQUIRREL_API SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
+SQUIRREL_API const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
+SQUIRREL_API const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
+SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err);
+SQUIRREL_API void sq_reseterror(HSQUIRRELVM v);
+SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v);
+
+/*raw object handling*/
+SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po);
+SQUIRREL_API void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj);
+SQUIRREL_API void sq_addref(HSQUIRRELVM v,HSQOBJECT *po);
+SQUIRREL_API SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po);
+SQUIRREL_API void sq_resetobject(HSQOBJECT *po);
+SQUIRREL_API const SQChar *sq_objtostring(HSQOBJECT *o);
+SQUIRREL_API SQBool sq_objtobool(HSQOBJECT *o);
+SQUIRREL_API SQInteger sq_objtointeger(HSQOBJECT *o);
+SQUIRREL_API SQFloat sq_objtofloat(HSQOBJECT *o);
+SQUIRREL_API SQRESULT sq_getobjtypetag(HSQOBJECT *o,SQUserPointer * typetag);
+
+/*GC*/
+SQUIRREL_API SQInteger sq_collectgarbage(HSQUIRRELVM v);
+
+/*serialization*/
+SQUIRREL_API SQRESULT sq_writeclosure(HSQUIRRELVM vm,SQWRITEFUNC writef,SQUserPointer up);
+SQUIRREL_API SQRESULT sq_readclosure(HSQUIRRELVM vm,SQREADFUNC readf,SQUserPointer up);
+
+/*mem allocation*/
+SQUIRREL_API void *sq_malloc(SQUnsignedInteger size);
+SQUIRREL_API void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize);
+SQUIRREL_API void sq_free(void *p,SQUnsignedInteger size);
+
+/*debug*/
+SQUIRREL_API SQRESULT sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si);
+SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v);
+
+/*UTILITY MACRO*/
+#define sq_isnumeric(o) ((o)._type&SQOBJECT_NUMERIC)
+#define sq_istable(o) ((o)._type==OT_TABLE)
+#define sq_isarray(o) ((o)._type==OT_ARRAY)
+#define sq_isfunction(o) ((o)._type==OT_FUNCPROTO)
+#define sq_isclosure(o) ((o)._type==OT_CLOSURE)
+#define sq_isgenerator(o) ((o)._type==OT_GENERATOR)
+#define sq_isnativeclosure(o) ((o)._type==OT_NATIVECLOSURE)
+#define sq_isstring(o) ((o)._type==OT_STRING)
+#define sq_isinteger(o) ((o)._type==OT_INTEGER)
+#define sq_isfloat(o) ((o)._type==OT_FLOAT)
+#define sq_isuserpointer(o) ((o)._type==OT_USERPOINTER)
+#define sq_isuserdata(o) ((o)._type==OT_USERDATA)
+#define sq_isthread(o) ((o)._type==OT_THREAD)
+#define sq_isnull(o) ((o)._type==OT_NULL)
+#define sq_isclass(o) ((o)._type==OT_CLASS)
+#define sq_isinstance(o) ((o)._type==OT_INSTANCE)
+#define sq_isbool(o) ((o)._type==OT_BOOL)
+#define sq_isweakref(o) ((o)._type==OT_WEAKREF)
+#define sq_type(o) ((o)._type)
+
+/* deprecated */
+#define sq_createslot(v,n) sq_newslot(v,n,SQFalse)
+
+#define SQ_OK (0)
+#define SQ_ERROR (-1)
+
+#define SQ_FAILED(res) (res<0)
+#define SQ_SUCCEEDED(res) (res>=0)
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQUIRREL_H_*/
diff --git a/src/squirrel/sqdbg/serialize_state.inl b/src/squirrel/sqdbg/serialize_state.inl
new file mode 100644 (file)
index 0000000..347ee63
--- /dev/null
@@ -0,0 +1,416 @@
+static const SQChar serialize_state_nut[] = {
+0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x6c, 0x6f, 
+0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 
+0x3d, 0x7b, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x3d, 0x30, 0x2c, 0x72, 0x65, 
+0x66, 0x73, 0x3d, 0x7b, 0x7d, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 
+0x6d, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 
+0x3c, 0x2d, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62, 
+0x6c, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x5d, 
+0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 
+0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 
+0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73, 
+0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 
+0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x77, 0x65, 0x61, 0x6b, 
+0x72, 0x65, 0x66, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 
+0x2c, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 
+0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 
+0x65, 0x66, 0x73, 0x28, 0x74, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73, 
+0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 
+0x66, 0x28, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74, 
+0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29, 
+0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0d, 
+0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x74, 0x79, 0x70, 
+0x65, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74, 
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x6f, 0x74, 0x79, 0x70, 
+0x65, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 
+0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x28, 0x74, 0x20, 0x69, 0x6e, 
+0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 
+0x66, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x6f, 
+0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 
+0x5b, 0x74, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 
+0x72, 0x65, 0x67, 0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b, 
+0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 
+0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 
+0x74, 0x28, 0x74, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 
+0x28, 0x6f, 0x2c, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3a, 0x28, 0x6f, 
+0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x09, 0x09, 
+0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20, 0x20, 
+0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 
+0x28, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20, 
+0x20, 0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 
+0x73, 0x28, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 
+0x20, 0x7d, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 
+0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 
+0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x76, 
+0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 
+0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 
+0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 
+0x65, 0x28, 0x76, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 
+0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 
+0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 
+0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 
+0x61, 0x73, 0x65, 0x20, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 
+0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e, 
+0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 
+0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x73, 
+0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x76, 0x5d, 
+0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x3b, 
+0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e, 
+0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 
+0x61, 0x73, 0x65, 0x20, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22, 0x3a, 
+0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 
+0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 
+0x65, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 
+0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 
+0x76, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x73, 
+0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09, 
+0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 
+0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 
+0x75, 0x72, 0x6e, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x3b, 0x0d, 
+0x0a, 0x09, 0x09, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0d, 
+0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 
+0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, 
+0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 
+0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3d, 0x7b, 0x0d, 0x0a, 0x09, 0x5b, 
+0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x6e, 0x22, 0x2c, 
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 
+0x5d, 0x3d, 0x22, 0x73, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 
+0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x5d, 0x3d, 0x22, 0x69, 0x22, 
+0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22, 
+0x5d, 0x3d, 0x22, 0x66, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x75, 
+0x73, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x3d, 0x22, 0x75, 
+0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 
+0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x3d, 0x22, 0x66, 0x6e, 0x22, 0x2c, 0x0d, 
+0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x5d, 0x3d, 
+0x22, 0x74, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72, 
+0x61, 0x79, 0x22, 0x5d, 0x3d, 0x22, 0x61, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 
+0x5b, 0x22, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 
+0x5d, 0x3d, 0x22, 0x67, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74, 
+0x68, 0x72, 0x65, 0x61, 0x64, 0x22, 0x5d, 0x3d, 0x22, 0x68, 0x22, 0x2c, 
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 
+0x65, 0x22, 0x5d, 0x3d, 0x22, 0x78, 0x22, 0x2c, 0x20, 0x0d, 0x0a, 0x09, 
+0x5b, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x3d, 0x22, 0x79, 
+0x22, 0x2c, 0x20, 0x20, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x62, 0x6f, 0x6f, 
+0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x62, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 
+0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x5d, 0x3d, 0x22, 
+0x77, 0x22, 0x20, 0x20, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 
+0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, 0x29, 0x3a, 
+0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 
+0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x74, 
+0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 
+0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x72, 0x65, 0x74, 0x75, 
+0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79, 
+0x70, 0x65, 0x73, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x0d, 0x0a, 0x09, 
+0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0d, 
+0x0a, 0x7d, 0x20, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 
+0x69, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f, 
+0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x66, 0x75, 
+0x6e, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 
+0x61, 0x6c, 0x20, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79, 
+0x70, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 
+0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x69, 0x6e, 0x73, 
+0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 
+0x09, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x54, 0x52, 0x59, 
+0x20, 0x54, 0x4f, 0x20, 0x55, 0x53, 0x45, 0x20, 0x5f, 0x6e, 0x65, 0x78, 
+0x74, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 
+0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 
+0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x0d, 0x0a, 0x09, 
+0x09, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 
+0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64, 0x78, 
+0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 
+0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 
+0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 
+0x09, 0x09, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 
+0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, 
+0x6f, 0x62, 0x6a, 0x2e, 0x67, 0x65, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 
+0x28, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7b, 0x0d, 
+0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 
+0x2c, 0x69, 0x64, 0x78, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x64, 0x78, 
+0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7d, 0x0d, 
+0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 
+0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d, 
+0x20, 0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x29, 0x20, 
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 
+0x6a, 0x2c, 0x22, 0x40, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x62, 0x6a, 
+0x2e, 0x72, 0x65, 0x66, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 
+0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 
+0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78, 
+0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 
+0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 
+0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64, 
+0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 
+0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x7d, 
+0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 
+0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x28, 
+0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 
+0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 
+0x68, 0x28, 0x69, 0x2c, 0x6f, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 
+0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x29, 0x0d, 
+0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 
+0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 
+0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x28, 0x69, 
+0x3d, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74, 
+0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x3f, 0x22, 0x72, 0x22, 0x3a, 0x70, 
+0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74, 
+0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 
+0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x5f, 0x74, 0x79, 0x70, 
+0x65, 0x6f, 0x66, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 
+0x20, 0x69, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x5f, 0x74, 
+0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 
+0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 
+0x22, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x22, 0x2c, 0x5f, 0x74, 0x79, 
+0x70, 0x65, 0x6f, 0x66, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 
+0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 
+0x28, 0x22, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x2e, 0x74, 0x6f, 0x73, 
+0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 
+0x09, 0x69, 0x66, 0x28, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x67, 
+0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 
+0x29, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x72, 
+0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, 
+0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6f, 0x62, 
+0x6a, 0x2c, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x7b, 
+0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x3a, 0x3a, 0x74, 
+0x79, 0x70, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3d, 0x3d, 0x20, 
+0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x0d, 
+0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 
+0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 
+0x6e, 0x74, 0x28, 0x22, 0x65, 0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 
+0x65, 0x28, 0x22, 0x6b, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x76, 0x22, 0x2c, 
+0x69, 0x64, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 
+0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x76, 
+0x74, 0x22, 0x2c, 0x22, 0x76, 0x22, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 
+0x64, 0x78, 0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, 
+0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x65, 
+0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 
+0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22, 
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 
+0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x76, 
+0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 
+0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x64, 0x2c, 0x65, 
+0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0d, 0x0a, 
+0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 
+0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x22, 0x72, 0x65, 0x74, 0x75, 
+0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 
+0x28, 0x22, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 
+0x61, 0x72, 0x61, 0x6d, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 
+0x0d, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70, 
+0x70, 0x65, 0x6e, 0x64, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x5b, 
+0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x5d, 0x29, 0x0d, 0x0a, 0x09, 0x6c, 
+0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x3d, 0x31, 
+0x3b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 
+0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 
+0x73, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x69, 0x21, 
+0x3d, 0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x20, 0x26, 0x26, 0x20, 0x69, 
+0x5b, 0x30, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x40, 0x27, 0x29, 0x7b, 
+0x20, 0x2f, 0x2f, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 
+0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x73, 0x74, 0x61, 
+0x72, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x40, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x66, 0x69, 0x72, 0x73, 0x74, 0x29, 
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, 
+0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 
+0x2b, 0x22, 0x2c, 0x22, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 
+0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x69, 0x72, 
+0x73, 0x74, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 
+0x64, 0x28, 0x76, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 
+0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 
+0x72, 0x63, 0x2b, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 
+0x7d, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 
+0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x2b, 0x22, 0x29, 
+0x7b, 0x5c, 0x6e, 0x22, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, 
+0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 
+0x2b, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x22, 0x2b, 
+0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2b, 0x22, 
+0x29, 0x5c, 0x6e, 0x7d, 0x22, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x74, 
+0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 
+0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x3d, 0x3a, 0x3a, 0x63, 0x6f, 0x6d, 
+0x70, 0x69, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x66, 
+0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 
+0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x73, 0x74, 0x61, 
+0x74, 0x75, 0x73, 0x3d, 0x22, 0x6f, 0x6b, 0x22, 0x20, 0x2c, 0x20, 0x76, 
+0x61, 0x6c, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x2e, 0x61, 0x63, 
+0x61, 0x6c, 0x6c, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x7d, 
+0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x63, 0x61, 0x74, 0x63, 
+0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 
+0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 
+0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f, 
+0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 
+0x0d, 0x0a, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x0d, 0x0a, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 
+0x2f, 0x2f, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 
+0x20, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x74, 
+0x79, 0x70, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x76, 
+0x61, 0x6c, 0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 
+0x76, 0x61, 0x6c, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x74, 
+0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, 
+0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x70, 0x61, 0x63, 0x6b, 
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 
+0x28, 0x76, 0x61, 0x6c, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 
+0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x76, 0x61, 0x6c, 
+0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x67, 0x65, 
+0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x2e, 
+0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 
+0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 
+0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x3d, 0x5b, 0x5d, 0x0d, 0x0a, 0x6c, 
+0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x33, 
+0x3b, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x69, 0x3b, 
+0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, 
+0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x4e, 0x55, 0x4d, 0x45, 0x52, 0x41, 0x54, 
+0x45, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x20, 
+0x57, 0x41, 0x54, 0x43, 0x48, 0x45, 0x53, 0x0d, 0x0a, 0x09, 0x77, 0x68, 
+0x69, 0x6c, 0x65, 0x28, 0x73, 0x69, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74, 
+0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x28, 0x6c, 
+0x65, 0x76, 0x65, 0x6c, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 
+0x09, 0x09, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x70, 0x65, 
+0x6e, 0x64, 0x28, 0x73, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x6c, 
+0x65, 0x76, 0x65, 0x6c, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 
+0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x56, 0x41, 0x4c, 0x55, 0x41, 
+0x54, 0x45, 0x20, 0x41, 0x4c, 0x4c, 0x20, 0x57, 0x41, 0x54, 0x43, 0x48, 
+0x45, 0x53, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 
+0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x3a, 0x3a, 0x67, 0x65, 0x74, 
+0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x5d, 
+0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 
+0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, 
+0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61, 
+0x6c, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d, 
+0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61, 
+0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54, 0x49, 
+0x56, 0x45, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 
+0x66, 0x28, 0x22, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20, 
+0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 
+0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 
+0x68, 0x65, 0x73, 0x20, 0x3c, 0x2d, 0x20, 0x7b, 0x7d, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 
+0x2c, 0x77, 0x61, 0x74, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x61, 
+0x74, 0x63, 0x68, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 
+0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54, 
+0x49, 0x56, 0x45, 0x22, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 
+0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x65, 0x76, 0x61, 0x6c, 
+0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x28, 0x76, 
+0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x2c, 
+0x77, 0x61, 0x74, 0x63, 0x68, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 
+0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e, 0x73, 0x74, 0x61, 
+0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x75, 
+0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, 0x76, 0x61, 0x6c, 
+0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e, 
+0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 
+0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c, 
+0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x20, 
+0x3c, 0x2d, 0x20, 0x7b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22, 
+0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 
+0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 
+0x2e, 0x65, 0x78, 0x70, 0x20, 0x3c, 0x2d, 0x20, 0x77, 0x61, 0x74, 0x63, 
+0x68, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 
+0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 
+0x68, 0x28, 0x69, 0x2c, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 
+0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 
+0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, 
+0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 
+0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 
+0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29, 0x3b, 0x0d, 
+0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, 
+0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29, 
+0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 
+0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, 
+0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 
+0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 
+0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 
+0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 
+0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, 
+0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 
+0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x66, 0x6e, 0x63, 0x22, 0x2c, 0x76, 
+0x61, 0x6c, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 
+0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 
+0x73, 0x72, 0x63, 0x22, 0x2c, 0x76, 0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63, 
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 
+0x75, 0x74, 0x65, 0x28, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x2c, 0x76, 
+0x61, 0x6c, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x6f, 0x73, 0x74, 
+0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 
+0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20, 
+0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 
+0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 
+0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 
+0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x6e, 
+0x61, 0x6d, 0x65, 0x22, 0x2c, 0x67, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x75, 
+0x65, 0x28, 0x69, 0x29, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 
+0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, 
+0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x74, 0x79, 
+0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c, 0x22, 0x2c, 0x76, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 
+0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x22, 0x77, 
+0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, 
+0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 
+0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 
+0x20, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 
+0x74, 0x28, 0x22, 0x77, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 
+0x22, 0x69, 0x64, 0x22, 0x2c, 0x69, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 
+0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 
+0x22, 0x65, 0x78, 0x70, 0x22, 0x2c, 0x76, 0x2e, 0x65, 0x78, 0x70, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 
+0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 
+0x73, 0x22, 0x2c, 0x76, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 
+0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72, 
+0x72, 0x6f, 0x72, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 
+0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 
+0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c, 
+0x22, 0x2c, 0x76, 0x2e, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 
+0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, 
+0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x77, 
+0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 
+0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, 0x22, 0x29, 
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 
+0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 
+0x22, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 
+0x0a, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 
+0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0d, 0x0a, 0x09, 0x73, 
+0x74, 0x61, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 
+0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x22, 0x63, 0x6f, 
+0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 
+0x22, 0x20, 0x69, 0x6e, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f, 
+0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29, 0x20, 0x3a, 
+0x3a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 
+0x61, 0x67, 0x65, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x63, 0x61, 0x74, 
+0x63, 0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x3a, 
+0x3a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x22, 0x45, 0x52, 0x52, 0x4f, 
+0x52, 0x22, 0x2b, 0x65, 0x2b, 0x22, 0x5c, 0x6e, 0x22, 0x29, 0x3b, 0x0d, 
+0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 
+};
diff --git a/src/squirrel/sqdbg/sqdbgserver.cpp b/src/squirrel/sqdbg/sqdbgserver.cpp
new file mode 100644 (file)
index 0000000..0227412
--- /dev/null
@@ -0,0 +1,634 @@
+#include <squirrel.h>
+#include <assert.h>
+#include <sqstdblob.h>
+#include "sqrdbg.h"
+#include "sqdbgserver.h"
+
+
+#ifndef _UNICODE
+#define scstrcpy strcpy
+#else
+#define scstrcpy wcscpy
+#endif
+struct XMLEscape{
+       const SQChar c;
+       const SQChar *esc;
+};
+
+#define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
+#define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
+
+XMLEscape g_escapes[]={
+       {_SC('<'),_SC("&lt;")},{'>',_SC("&gt;")},{_SC('&'),_SC("&amp;")},{_SC('\''),_SC("&apos;")},{_SC('\"'),_SC("&quot;")},{_SC('\n'),_SC("&quot;n")},{_SC('\r'),_SC("&quot;r")},{0, NULL}
+};
+
+const SQChar *IntToString(int n)
+{
+       static SQChar temp[256];
+       scsprintf(temp,_SC("%d"),n);
+       return temp;
+}
+
+SQInteger debug_hook(HSQUIRRELVM v);
+SQInteger error_handler(HSQUIRRELVM v);
+
+SQInteger beginelement(HSQUIRRELVM v)
+{
+       SQUserPointer up;
+       const SQChar *name;
+       sq_getuserpointer(v,-1,&up);
+       SQDbgServer *self = (SQDbgServer*)up;
+       sq_getuserpointer(v,-1,&up);
+       sq_getstring(v,2,&name);
+       self->BeginElement(name);
+       return 0;
+}
+
+SQInteger endelement(HSQUIRRELVM v)
+{
+       SQUserPointer up;
+       const SQChar *name;
+       sq_getuserpointer(v,-1,&up);
+       SQDbgServer *self = (SQDbgServer*)up;
+       sq_getuserpointer(v,-1,&up);
+       sq_getstring(v,2,&name);
+       self->EndElement(name);
+       return 0;
+}
+
+SQInteger attribute(HSQUIRRELVM v)
+{
+       SQUserPointer up;
+       const SQChar *name,*value;
+       sq_getuserpointer(v,-1,&up);
+       SQDbgServer *self = (SQDbgServer*)up;
+       sq_getuserpointer(v,-1,&up);
+       sq_getstring(v,2,&name);
+       sq_getstring(v,3,&value);
+       self->Attribute(name,value);
+       return 0;
+}
+
+const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)
+{
+
+       SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));
+       SQChar *dest=temp;
+       while(*s!=_SC('\0')){
+               int i=0;
+               bool escaped=false;
+               while(g_escapes[i].esc!=NULL){
+                       if(*s==g_escapes[i].c){
+                               scstrcpy(dest,g_escapes[i].esc);
+                               dest+=scstrlen(g_escapes[i].esc);
+                               escaped=true;
+                               break;
+                       }
+                       i++;
+               }
+               if(!escaped){*dest=*s;*dest++;}
+               *s++;
+       }
+       *dest=_SC('\0');
+       return temp;
+}
+
+SQDbgServer::SQDbgServer(HSQUIRRELVM v)
+{
+       _ready = false;
+       _nestedcalls = 0;
+       _autoupdate = false;
+       _v = v;
+       _state = eDBG_Running;
+       _accept = INVALID_SOCKET;
+       _endpoint = INVALID_SOCKET;
+       _maxrecursion = 10;
+       sq_resetobject(&_debugroot);
+}
+
+SQDbgServer::~SQDbgServer()
+{
+       sq_release(_v,&_debugroot);
+       if(_accept != INVALID_SOCKET)
+               sqdbg_closesocket(_accept);
+       if(_endpoint != INVALID_SOCKET)
+               sqdbg_closesocket(_endpoint);
+}
+
+bool SQDbgServer::Init()
+{
+       //creates  an environment table for the debugger
+
+       sq_newtable(_v);
+       sq_getstackobj(_v,-1,&_debugroot);
+       sq_addref(_v,&_debugroot);
+
+       //creates a emptyslot to store the watches
+       sq_pushstring(_v,_SC("watches"),-1);
+       sq_pushnull(_v);
+       sq_createslot(_v,-3);
+
+       sq_pushstring(_v,_SC("beginelement"),-1);
+       sq_pushuserpointer(_v,this);
+       sq_newclosure(_v,beginelement,1);
+       sq_setparamscheck(_v,2,_SC(".s"));
+       sq_createslot(_v,-3);
+
+       sq_pushstring(_v,_SC("endelement"),-1);
+       sq_pushuserpointer(_v,this);
+       sq_newclosure(_v,endelement,1);
+       sq_setparamscheck(_v,2,_SC(".s"));
+       sq_createslot(_v,-3);
+
+       sq_pushstring(_v,_SC("attribute"),-1);
+       sq_pushuserpointer(_v,this);
+       sq_newclosure(_v,attribute,1);
+       sq_setparamscheck(_v,3,_SC(".ss"));
+       sq_createslot(_v,-3);
+
+       sq_pop(_v,1);
+
+       //stores debug hook and error handler in the registry
+       sq_pushregistrytable(_v);
+
+       sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+       sq_pushuserpointer(_v,this);
+       sq_newclosure(_v,debug_hook,1);
+       sq_createslot(_v,-3);
+
+       sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+       sq_pushuserpointer(_v,this);
+       sq_newclosure(_v,error_handler,1);
+       sq_createslot(_v,-3);
+
+
+       sq_pop(_v,1);
+
+       //sets the error handlers
+       SetErrorHandlers();
+       return true;
+}
+
+bool SQDbgServer::ReadMsg()
+{
+       return false;
+}
+
+void SQDbgServer::BusyWait()
+{
+       while( !ReadMsg() )
+               sleep(0);
+}
+
+void SQDbgServer::SendChunk(const SQChar *chunk)
+{
+       char *buf=NULL;
+       int buf_len=0;
+#ifdef _UNICODE
+       buf_len=(int)scstrlen(chunk)+1;
+       buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
+       wcstombs((char *)buf,chunk,buf_len);
+#else
+       buf_len=(int)scstrlen(chunk);
+       buf=(char *)chunk;
+#endif
+       send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
+}
+
+
+void SQDbgServer::Terminated()
+{
+       BeginElement(_SC("terminated"));
+       EndElement(_SC("terminated"));
+       ::usleep(200);
+}
+
+void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)
+{
+       switch(_state){
+       case eDBG_Running:
+               if(type==_SC('l') && _breakpoints.size()) {
+                       BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
+                       if(itr != _breakpoints.end()) {
+                               Break(line,src,_SC("breakpoint"));
+                               BreakExecution();
+                       }
+               }
+               break;
+       case eDBG_Suspended:
+               _nestedcalls=0;
+       case eDBG_StepOver:
+               switch(type){
+               case _SC('l'):
+                       if(_nestedcalls==0) {
+                               Break(line,src,_SC("step"));
+                               BreakExecution();
+                       }
+                       break;
+               case _SC('c'):
+                       _nestedcalls++;
+                       break;
+               case _SC('r'):
+                       if(_nestedcalls==0){
+                               _nestedcalls=0;
+
+                       }else{
+                               _nestedcalls--;
+                       }
+                       break;
+               }
+               break;
+       case eDBG_StepInto:
+               switch(type){
+               case _SC('l'):
+                       _nestedcalls=0;
+                       Break(line,src,_SC("step"));
+                       BreakExecution();
+                       break;
+
+               }
+               break;
+       case eDBG_StepReturn:
+               switch(type){
+               case _SC('l'):
+                       break;
+               case _SC('c'):
+                       _nestedcalls++;
+                       break;
+               case _SC('r'):
+                       if(_nestedcalls==0){
+                               _nestedcalls=0;
+                               _state=eDBG_StepOver;
+                       }else{
+                               _nestedcalls--;
+                       }
+
+                       break;
+               }
+               break;
+       case eDBG_Disabled:
+               break;
+       }
+}
+
+
+#define MSG_ID(x,y) ((y<<8)|x)
+//ab Add Breakpoint
+//rb Remove Breakpoint
+//sp Suspend
+void SQDbgServer::ParseMsg(const char *msg)
+{
+
+       switch(*((unsigned short *)msg)){
+               case MSG_ID('a','b'): {
+                       BreakPoint bp;
+                       if(ParseBreakpoint(msg+3,bp)){
+                               AddBreakpoint(bp);
+                               scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
+                       }
+                       else
+                               scprintf(_SC("error parsing add breakpoint"));
+                                                        }
+                       break;
+               case MSG_ID('r','b'): {
+                       BreakPoint bp;
+                       if(ParseBreakpoint(msg+3,bp)){
+                               RemoveBreakpoint(bp);
+                               scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
+                       }else
+                               scprintf(_SC("error parsing remove breakpoint"));
+                                                       }
+                       break;
+               case MSG_ID('g','o'):
+                       if(_state!=eDBG_Running){
+                               _state=eDBG_Running;
+                               BeginDocument();
+                                       BeginElement(_SC("resumed"));
+                                       EndElement(_SC("resumed"));
+                               EndDocument();
+//                             Send(_SC("<resumed/>\r\n"));
+                               scprintf(_SC("go (execution resumed)\n"));
+                       }
+                       break;
+               case MSG_ID('s','p'):
+                       if(_state!=eDBG_Suspended){
+                               _state=eDBG_Suspended;
+                               scprintf(_SC("suspend\n"));
+                       }
+                       break;
+               case MSG_ID('s','o'):
+                       if(_state==eDBG_Suspended){
+                               _state=eDBG_StepOver;
+                       }
+                       break;
+               case MSG_ID('s','i'):
+                       if(_state==eDBG_Suspended){
+                               _state=eDBG_StepInto;
+                               scprintf(_SC("step into\n"));
+                       }
+                       break;
+               case MSG_ID('s','r'):
+                       if(_state==eDBG_Suspended){
+                               _state=eDBG_StepReturn;
+                               scprintf(_SC("step return\n"));
+                       }
+                       break;
+               case MSG_ID('d','i'):
+                       if(_state!=eDBG_Disabled){
+                               _state=eDBG_Disabled;
+                               scprintf(_SC("disabled\n"));
+                       }
+                       break;
+               case MSG_ID('a','w'): {
+                       Watch w;
+                       if(ParseWatch(msg+3,w))
+                       {
+                               AddWatch(w);
+                               scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
+                       }
+                       else
+                               scprintf(_SC("error parsing add watch"));
+                                                               }
+                       break;
+               case MSG_ID('r','w'): {
+                       int id;
+                       if(ParseRemoveWatch(msg+3,id))
+                       {
+                               RemoveWatch(id);
+                               scprintf(_SC("added watch %d\n"),id);
+                       }
+                       else
+                               scprintf(_SC("error parsing remove watch"));
+                                                               }
+                       break;
+               case MSG_ID('t','r'):
+                       scprintf(_SC("terminate from user\n"));
+                       break;
+               case MSG_ID('r','d'):
+                       scprintf(_SC("ready\n"));
+                       _ready=true;
+                       break;
+               default:
+                       scprintf(_SC("unknown packet"));
+
+       }
+}
+
+/*
+       see copyright notice in sqrdbg.h
+*/
+bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
+{
+       static char stemp[MAX_BP_PATH];
+       char *ep=NULL;
+       out._line=strtoul(msg,&ep,16);
+       if(ep==msg || (*ep)!=':')return false;
+
+       char *dest=stemp;
+       ep++;
+       while((*ep)!='\n' && (*ep)!='\0')
+       {
+               *dest=*ep;
+               *dest++;*ep++;
+       }
+       *dest='\0';
+       *dest++;
+       *dest='\0';
+#ifdef _UNICODE
+       int len=(int)strlen(stemp);
+       SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));
+       size_t destlen=mbstowcs(p,stemp,len);
+       p[destlen]=_SC('\0');
+       out._src=p;
+#else
+       out._src=stemp;
+#endif
+       return true;
+}
+
+bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
+{
+       char *ep=NULL;
+       out._id=strtoul(msg,&ep,16);
+       if(ep==msg || (*ep)!=':')return false;
+
+       //char *dest=out._src;
+       ep++;
+       while((*ep)!='\n' && (*ep)!='\0')
+       {
+               out._exp.append(1,*ep);
+               *ep++;
+       }
+       return true;
+}
+
+bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)
+{
+       char *ep=NULL;
+       id=strtoul(msg,&ep,16);
+       if(ep==msg)return false;
+       return true;
+}
+
+
+void SQDbgServer::BreakExecution()
+{
+       _state=eDBG_Suspended;
+       while(_state==eDBG_Suspended){
+               if(SQ_FAILED(sq_rdbg_update(this)))
+                       exit(0);
+               usleep(10);
+       }
+}
+
+//COMMANDS
+void SQDbgServer::AddBreakpoint(BreakPoint &bp)
+{
+       _breakpoints.insert(bp);
+       BeginDocument();
+               BeginElement(_SC("addbreakpoint"));
+                       Attribute(_SC("line"),IntToString(bp._line));
+                       Attribute(_SC("src"),bp._src.c_str());
+               EndElement(_SC("addbreakpoint"));
+       EndDocument();
+}
+
+void SQDbgServer::AddWatch(Watch &w)
+{
+       _watches.insert(w);
+}
+
+void SQDbgServer::RemoveWatch(int id)
+{
+       WatchSetItor itor=_watches.find(Watch(id,_SC("")));
+       if(itor==_watches.end()){
+               BeginDocument();
+               BeginElement(_SC("error"));
+                       Attribute(_SC("desc"),_SC("the watch does not exists"));
+               EndElement(_SC("error"));
+       EndDocument();
+       }
+       else{
+               _watches.erase(itor);
+               scprintf(_SC("removed watch %d\n"),id);
+       }
+}
+
+void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
+{
+       BreakPointSetItor itor=_breakpoints.find(bp);
+       if(itor==_breakpoints.end()){
+               BeginDocument();
+                       BeginElement(_SC("break"));
+                               Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
+                       EndElement(_SC("break"));
+               EndDocument();
+       }
+       else{
+               BeginDocument();
+                       BeginElement(_SC("removebreakpoint"));
+                               Attribute(_SC("line"),IntToString(bp._line));
+                               Attribute(_SC("src"),bp._src.c_str());
+                       EndElement(_SC("removebreakpoint"));
+               EndDocument();
+               _breakpoints.erase(itor);
+       }
+}
+
+void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)
+{
+       if(!error){
+               BeginDocument();
+                       BeginElement(_SC("break"));
+                               Attribute(_SC("line"),IntToString(line));
+                               Attribute(_SC("src"),src);
+                               Attribute(_SC("type"),type);
+                               SerializeState();
+                       EndElement(_SC("break"));
+               EndDocument();
+       }else{
+               BeginDocument();
+                       BeginElement(_SC("break"));
+                               Attribute(_SC("line"),IntToString(line));
+                               Attribute(_SC("src"),src);
+                               Attribute(_SC("type"),type);
+                               Attribute(_SC("error"),error);
+                               SerializeState();
+                       EndElement(_SC("break"));
+               EndDocument();
+       }
+}
+
+void SQDbgServer::SerializeState()
+{
+       sq_pushnull(_v);
+       sq_setdebughook(_v);
+       sq_pushnull(_v);
+       sq_seterrorhandler(_v);
+       const SQChar *sz;
+       sq_pushobject(_v,_serializefunc);
+       sq_pushobject(_v,_debugroot);
+       sq_pushstring(_v,_SC("watches"),-1);
+       sq_newtable(_v);
+       for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
+       {
+               sq_pushinteger(_v,i->_id);
+               sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());
+               sq_createslot(_v,-3);
+       }
+       sq_rawset(_v,-3);
+       if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue,SQTrue))){
+               if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))
+                       SendChunk(sz);
+       }
+       sq_pop(_v,2);
+
+       SetErrorHandlers();
+}
+
+
+void SQDbgServer::SetErrorHandlers()
+{
+       sq_pushregistrytable(_v);
+       sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+       sq_rawget(_v,-2);
+       sq_setdebughook(_v);
+       sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+       sq_rawget(_v,-2);
+       sq_seterrorhandler(_v);
+       sq_pop(_v,1);
+}
+
+void SQDbgServer::BeginElement(const SQChar *name)
+{
+       _xmlcurrentement++;
+       XMLElementState *self = &xmlstate[_xmlcurrentement];
+       scstrcpy(self->name,name);
+       self->haschildren = false;
+       if(_xmlcurrentement > 0) {
+               XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
+               if(!parent->haschildren) {
+                       SendChunk(_SC(">")); // closes the parent tag
+                       parent->haschildren = true;
+               }
+       }
+       _scratchstring.resize(2+scstrlen(name));
+       scsprintf(&_scratchstring[0],_SC("<%s"),name);
+       SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
+{
+       XMLElementState *self = &xmlstate[_xmlcurrentement];
+       assert(!self->haschildren); //cannot have attributes if already has children
+       const SQChar *escval = escape_xml(value);
+       _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));
+       scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
+       SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::EndElement(const SQChar *name)
+{
+       XMLElementState *self = &xmlstate[_xmlcurrentement];
+       assert(scstrcmp(self->name,name) == 0);
+       if(self->haschildren) {
+               _scratchstring.resize(4+scstrlen(name));
+               scsprintf(&_scratchstring[0],_SC("</%s>"),name);
+               SendChunk(&_scratchstring[0]);
+
+       }
+       else {
+               SendChunk(_SC("/>"));
+       }
+       _xmlcurrentement--;
+}
+
+void SQDbgServer::EndDocument()
+{
+       SendChunk(_SC("\r\n"));
+}
+
+//this can be done much better/faster(do we need that?)
+const SQChar *SQDbgServer::escape_xml(const SQChar *s)
+{
+       SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));
+       SQChar *dest=temp;
+       while(*s!=_SC('\0')){
+               int i=0;
+               bool escaped=false;
+               while(g_escapes[i].esc!=NULL){
+                       if(*s==g_escapes[i].c){
+                               scstrcpy(dest,g_escapes[i].esc);
+                               dest+=scstrlen(g_escapes[i].esc);
+                               escaped=true;
+                               break;
+                       }
+                       i++;
+               }
+               if(!escaped){*dest=*s;*dest++;}
+               *s++;
+       }
+       *dest=_SC('\0');
+       return temp;
+
+}
diff --git a/src/squirrel/sqdbg/sqdbgserver.h b/src/squirrel/sqdbg/sqdbgserver.h
new file mode 100644 (file)
index 0000000..4d91192
--- /dev/null
@@ -0,0 +1,160 @@
+#ifndef _SQ_DBGSERVER_H_
+#define _SQ_DBGSERVER_H_
+
+#define MAX_BP_PATH 512
+#define MAX_MSG_LEN 2049
+
+#include <set>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+#include <winsock.h>
+#define sqdbg_closesocket(x) closesocket((x))
+typedef socklen_t int;
+#else
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#define sqdbg_closesocket(x) close((x))
+typedef int SOCKET;
+typedef struct timeval TIMEVAL;
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+#endif
+
+typedef std::basic_string<SQChar> SQDBGString;
+
+inline bool dbg_less(const SQChar *x,const SQChar *y)
+{
+       // [SuperTux] commented out to avoid compiler warning
+       //int n = 0;
+       do {
+               int xl = *x == '\\' ? '/' : tolower(*x);
+               int yl = *y == '\\' ? '/' : tolower(*y);
+               int diff = xl - yl;
+               if(diff != 0)
+                       return diff > 0?true:false;
+               x++; y++;
+       }while(*x != 0 && *y != 0);
+       return false;
+}
+
+struct BreakPoint{
+       BreakPoint(){_line=0;}
+       BreakPoint(int line, const SQChar *src){ _line = line; _src = src; }
+       BreakPoint(const BreakPoint& bp){ _line = bp._line; _src=bp._src; }
+       inline bool operator<(const BreakPoint& bp) const
+       {
+               if(_line<bp._line)
+                       return true;
+               if(_line==bp._line){
+                       return dbg_less(_src.c_str(),bp._src.c_str());
+               }
+               return false;
+       }
+
+       int _line;
+       SQDBGString _src;
+};
+
+struct Watch{
+       Watch() { _id = 0; }
+       Watch(int id,const SQChar *exp) { _id = id; _exp = exp; }
+       Watch(const Watch &w) { _id = w._id; _exp = w._exp; }
+       bool operator<(const Watch& w) const { return _id<w._id; }
+       bool operator==(const Watch& w) const { return _id == w._id; }
+       int _id;
+       SQDBGString _exp;
+};
+
+typedef std::set<BreakPoint> BreakPointSet;
+typedef BreakPointSet::iterator BreakPointSetItor;
+
+typedef std::set<Watch> WatchSet;
+typedef WatchSet::iterator WatchSetItor;
+
+typedef std::vector<SQChar> SQCharVec;
+struct SQDbgServer{
+public:
+       enum eDbgState{
+               eDBG_Running,
+               eDBG_StepOver,
+               eDBG_StepInto,
+               eDBG_StepReturn,
+               eDBG_Suspended,
+               eDBG_Disabled,
+       };
+
+       SQDbgServer(HSQUIRRELVM v);
+       ~SQDbgServer();
+       bool Init();
+       //returns true if a message has been received
+       bool WaitForClient();
+       bool ReadMsg();
+       void BusyWait();
+       void Hook(int type,int line,const SQChar *src,const SQChar *func);
+       void ParseMsg(const char *msg);
+       bool ParseBreakpoint(const char *msg,BreakPoint &out);
+       bool ParseWatch(const char *msg,Watch &out);
+       bool ParseRemoveWatch(const char *msg,int &id);
+       void Terminated();
+       //
+       void BreakExecution();
+       void Send(const SQChar *s,...);
+       void SendChunk(const SQChar *chunk);
+       void Break(int line,const SQChar *src,const SQChar *type,const SQChar *error=NULL);
+
+
+       void SerializeState();
+       //COMMANDS
+       void AddBreakpoint(BreakPoint &bp);
+       void AddWatch(Watch &w);
+       void RemoveWatch(int id);
+       void RemoveBreakpoint(BreakPoint &bp);
+
+       //
+       void SetErrorHandlers();
+
+       //XML RELATED STUFF///////////////////////
+       #define MAX_NESTING 10
+       struct XMLElementState {
+               SQChar name[256];
+               bool haschildren;
+       };
+
+       XMLElementState xmlstate[MAX_NESTING];
+       int _xmlcurrentement;
+
+       void BeginDocument() { _xmlcurrentement = -1; }
+       void BeginElement(const SQChar *name);
+       void Attribute(const SQChar *name, const SQChar *value);
+       void EndElement(const SQChar *name);
+       void EndDocument();
+
+       const SQChar *escape_xml(const SQChar *x);
+       //////////////////////////////////////////////
+       HSQUIRRELVM _v;
+       HSQOBJECT _debugroot;
+       eDbgState _state;
+       SOCKET _accept;
+       SOCKET _endpoint;
+       BreakPointSet _breakpoints;
+       WatchSet _watches;
+       int _recursionlevel;
+       int _maxrecursion;
+       int _nestedcalls;
+       bool _ready;
+       bool _autoupdate;
+       HSQOBJECT _serializefunc;
+       SQCharVec _scratchstring;
+
+};
+
+#endif //_SQ_DBGSERVER_H_
diff --git a/src/squirrel/sqdbg/sqrdbg.cpp b/src/squirrel/sqdbg/sqrdbg.cpp
new file mode 100644 (file)
index 0000000..7bf3b38
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+       see copyright notice in sqrdbg.h
+*/
+#include <squirrel.h>
+#include "sqrdbg.h"
+#include "sqdbgserver.h"
+SQInteger debug_hook(HSQUIRRELVM v);
+SQInteger error_handler(HSQUIRRELVM v);
+
+#include "serialize_state.inl"
+
+HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate)
+{
+       sockaddr_in bindaddr;
+#ifdef _WIN32
+       WSADATA wsadata;
+       if (WSAStartup (MAKEWORD(1,1), &wsadata) != 0){
+               return NULL;
+       }
+#endif
+
+       SQDbgServer *rdbg = new SQDbgServer(v);
+       rdbg->_autoupdate = autoupdate?true:false;
+       rdbg->_accept = socket(AF_INET,SOCK_STREAM,0);
+       bindaddr.sin_family = AF_INET;
+       bindaddr.sin_port = htons(port);
+       bindaddr.sin_addr.s_addr = htonl (INADDR_ANY);
+       if(bind(rdbg->_accept,(sockaddr*)&bindaddr,sizeof(bindaddr))==SOCKET_ERROR){
+               delete rdbg;
+               sq_throwerror(v,_SC("failed to bind the socket"));
+               return NULL;
+       }
+       if(!rdbg->Init()) {
+               delete rdbg;
+               sq_throwerror(v,_SC("failed to initialize the debugger"));
+               return NULL;
+       }
+
+    return rdbg;
+}
+
+SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg)
+{
+       if(SQ_FAILED(sq_compilebuffer(rdbg->_v,serialize_state_nut,(SQInteger)scstrlen(serialize_state_nut),_SC("SERIALIZE_STATE"),SQFalse))) {
+               sq_throwerror(rdbg->_v,_SC("error compiling the serialization function"));
+       }
+       sq_getstackobj(rdbg->_v,-1,&rdbg->_serializefunc);
+       sq_addref(rdbg->_v,&rdbg->_serializefunc);
+       sq_pop(rdbg->_v,1);
+
+       sockaddr_in cliaddr;
+       socklen_t addrlen=sizeof(cliaddr);
+       if(listen(rdbg->_accept,0)==SOCKET_ERROR)
+               return sq_throwerror(rdbg->_v,_SC("error on listen(socket)"));
+       rdbg->_endpoint = accept(rdbg->_accept,(sockaddr*)&cliaddr,&addrlen);
+       //do not accept any other connection
+       sqdbg_closesocket(rdbg->_accept);
+       rdbg->_accept = INVALID_SOCKET;
+       if(rdbg->_endpoint==INVALID_SOCKET){
+               return sq_throwerror(rdbg->_v,_SC("error accept(socket)"));
+       }
+       while(!rdbg->_ready){
+               sq_rdbg_update(rdbg);
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg)
+{
+       TIMEVAL time;
+       time.tv_sec=0;
+       time.tv_usec=0;
+       fd_set read_flags;
+    FD_ZERO(&read_flags);
+       FD_SET(rdbg->_endpoint, &read_flags);
+       select(FD_SETSIZE, &read_flags, NULL, NULL, &time);
+
+       if(FD_ISSET(rdbg->_endpoint,&read_flags)){
+               char temp[1024];
+               int size=0;
+               char c,prev=0;
+               memset(&temp,0,sizeof(temp));
+               int res;
+               FD_CLR(rdbg->_endpoint, &read_flags);
+               while((res = recv(rdbg->_endpoint,&c,1,0))>0){
+
+                       if(c=='\n')break;
+                       if(c!='\r'){
+                               temp[size]=c;
+                               prev=c;
+                               size++;
+                       }
+               }
+               switch(res){
+               case 0:
+                       return sq_throwerror(rdbg->_v,_SC("disconnected"));
+               case SOCKET_ERROR:
+                       return sq_throwerror(rdbg->_v,_SC("socket error"));
+        }
+
+               temp[size]=0;
+               temp[size+1]=0;
+               rdbg->ParseMsg(temp);
+       }
+       return SQ_OK;
+}
+
+SQInteger debug_hook(HSQUIRRELVM v)
+{
+       SQUserPointer up;
+       SQInteger event_type,line;
+       const SQChar *src,*func;
+       sq_getinteger(v,2,&event_type);
+       sq_getstring(v,3,&src);
+       sq_getinteger(v,4,&line);
+       sq_getstring(v,5,&func);
+       sq_getuserpointer(v,-1,&up);
+       HSQREMOTEDBG rdbg = (HSQREMOTEDBG)up;
+       rdbg->Hook(event_type,line,src,func);
+       if(rdbg->_autoupdate) {
+               if(SQ_FAILED(sq_rdbg_update(rdbg)))
+                       return sq_throwerror(v,_SC("socket failed"));
+       }
+       return 0;
+}
+
+SQInteger error_handler(HSQUIRRELVM v)
+{
+       SQUserPointer up;
+       const SQChar *sErr=NULL;
+       const SQChar *fn=_SC("unknown");
+       const SQChar *src=_SC("unknown");
+       int line=-1;
+       SQStackInfos si;
+       sq_getuserpointer(v,-1,&up);
+       HSQREMOTEDBG rdbg=(HSQREMOTEDBG)up;
+       if(SQ_SUCCEEDED(sq_stackinfos(v,1,&si)))
+       {
+               if(si.funcname)fn=si.funcname;
+               if(si.source)src=si.source;
+               line=si.line;
+               scprintf(_SC("*FUNCTION [%s] %s line [%d]\n"),fn,src,si.line);
+       }
+       if(sq_gettop(v)>=1){
+               if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr)))       {
+                       scprintf(_SC("\nAN ERROR HAS OCCURED [%s]\n"),sErr);
+                       rdbg->Break(si.line,src,_SC("error"),sErr);
+               }
+               else{
+                       scprintf(_SC("\nAN ERROR HAS OCCURED [unknown]\n"));
+                       rdbg->Break(si.line,src,_SC("error"),_SC("unknown"));
+               }
+       }
+       rdbg->BreakExecution();
+       return 0;
+}
+
+
+SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg)
+{
+       delete rdbg;
+#ifdef _WIN32
+       WSACleanup();
+#endif
+       return SQ_OK;
+}
diff --git a/src/squirrel/sqdbg/sqrdbg.h b/src/squirrel/sqdbg/sqrdbg.h
new file mode 100644 (file)
index 0000000..6302790
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+Copyright (c) 2003-2005 Alberto Demichelis
+
+This software is provided 'as-is', without any
+express or implied warranty. In no event will the
+authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software
+for any purpose, including commercial applications,
+and to alter it and redistribute it freely, subject
+to the following restrictions:
+
+               1. The origin of this software must not be
+               misrepresented; you must not claim that
+               you wrote the original software. If you
+               use this software in a product, an
+               acknowledgment in the product
+               documentation would be appreciated but is
+               not required.
+
+               2. Altered source versions must be plainly
+               marked as such, and must not be
+               misrepresented as being the original
+               software.
+
+               3. This notice may not be removed or
+               altered from any source distribution.
+
+*/
+#ifndef _SQ_RDBG_H_
+#define _SQ_RDBG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct SQDbgServer;
+typedef SQDbgServer* HSQREMOTEDBG;
+
+HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate);
+SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg);
+SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg);
+SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif //_SQ_RDBG_H_
diff --git a/src/squirrel/sqstdlib/sqstdaux.cpp b/src/squirrel/sqstdlib/sqstdaux.cpp
new file mode 100644 (file)
index 0000000..6d3ea31
--- /dev/null
@@ -0,0 +1,129 @@
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <sqstdaux.h>
+#include <assert.h>
+
+void sqstd_printcallstack(HSQUIRRELVM v)
+{
+       SQPRINTFUNCTION pf = sq_getprintfunc(v);
+       if(pf) {
+               SQStackInfos si;
+               SQInteger i;
+               SQFloat f;
+               const SQChar *s;
+               SQInteger level=1; //1 is to skip this function that is level 0
+               const SQChar *name=0; 
+               SQInteger seq=0;
+               pf(v,_SC("\nCALLSTACK\n"));
+               while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
+               {
+                       const SQChar *fn=_SC("unknown");
+                       const SQChar *src=_SC("unknown");
+                       if(si.funcname)fn=si.funcname;
+                       if(si.source)src=si.source;
+                       pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line);
+                       level++;
+               }
+               level=0;
+               pf(v,_SC("\nLOCALS\n"));
+
+               for(level=0;level<10;level++){
+                       seq=0;
+                       while((name = sq_getlocal(v,level,seq)))
+                       {
+                               seq++;
+                               switch(sq_gettype(v,-1))
+                               {
+                               case OT_NULL:
+                                       pf(v,_SC("[%s] NULL\n"),name);
+                                       break;
+                               case OT_INTEGER:
+                                       sq_getinteger(v,-1,&i);
+                                       pf(v,_SC("[%s] %d\n"),name,i);
+                                       break;
+                               case OT_FLOAT:
+                                       sq_getfloat(v,-1,&f);
+                                       pf(v,_SC("[%s] %.14g\n"),name,f);
+                                       break;
+                               case OT_USERPOINTER:
+                                       pf(v,_SC("[%s] USERPOINTER\n"),name);
+                                       break;
+                               case OT_STRING:
+                                       sq_getstring(v,-1,&s);
+                                       pf(v,_SC("[%s] \"%s\"\n"),name,s);
+                                       break;
+                               case OT_TABLE:
+                                       pf(v,_SC("[%s] TABLE\n"),name);
+                                       break;
+                               case OT_ARRAY:
+                                       pf(v,_SC("[%s] ARRAY\n"),name);
+                                       break;
+                               case OT_CLOSURE:
+                                       pf(v,_SC("[%s] CLOSURE\n"),name);
+                                       break;
+                               case OT_NATIVECLOSURE:
+                                       pf(v,_SC("[%s] NATIVECLOSURE\n"),name);
+                                       break;
+                               case OT_GENERATOR:
+                                       pf(v,_SC("[%s] NATIVECLOSURE\n"),name);
+                                       break;
+                               case OT_USERDATA:
+                                       pf(v,_SC("[%s] USERDATA\n"),name);
+                                       break;
+                               case OT_THREAD:
+                                       pf(v,_SC("[%s] THREAD\n"),name);
+                                       break;
+                               case OT_CLASS:
+                                       pf(v,_SC("[%s] CLASS\n"),name);
+                                       break;
+                               case OT_INSTANCE:
+                                       pf(v,_SC("[%s] INSTANCE\n"),name);
+                                       break;
+                               case OT_WEAKREF:
+                                       pf(v,_SC("[%s] WEAKREF\n"),name);
+                                       break;
+                               case OT_BOOL:{
+                                       sq_getinteger(v,-1,&i);
+                                       pf(v,_SC("[%s] %s\n"),name,i?_SC("true"):_SC("false"));
+                                                        }
+                                       break;
+                               default: assert(0); break;
+                               }
+                               sq_pop(v,1);
+                       }
+               }
+       }
+}
+
+static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
+{
+       SQPRINTFUNCTION pf = sq_getprintfunc(v);
+       if(pf) {
+               const SQChar *sErr = 0;
+               if(sq_gettop(v)>=1) {
+                       if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr)))       {
+                               pf(v,_SC("\nAN ERROR HAS OCCURED [%s]\n"),sErr);
+                       }
+                       else{
+                               pf(v,_SC("\nAN ERROR HAS OCCURED [unknown]\n"));
+                       }
+                       sqstd_printcallstack(v);
+               }
+       }
+       return 0;
+}
+
+void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column)
+{
+       SQPRINTFUNCTION pf = sq_getprintfunc(v);
+       if(pf) {
+               pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr);
+       }
+}
+
+void sqstd_seterrorhandlers(HSQUIRRELVM v)
+{
+       sq_setcompilererrorhandler(v,_sqstd_compiler_error);
+       sq_newclosure(v,_sqstd_aux_printerror,0);
+       sq_seterrorhandler(v);
+}
diff --git a/src/squirrel/sqstdlib/sqstdblob.cpp b/src/squirrel/sqstdlib/sqstdblob.cpp
new file mode 100644 (file)
index 0000000..f1a9f00
--- /dev/null
@@ -0,0 +1,251 @@
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include <string.h>
+#include <sqstdblob.h>
+#include "sqstdstream.h"
+#include "sqstdblobimpl.h"
+
+#define SQSTD_BLOB_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000002)
+
+//Blob
+
+
+#define SETUP_BLOB(v) \
+       SQBlob *self = NULL; \
+       { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \
+               return SQ_ERROR; }
+
+
+static SQInteger _blob_resize(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       SQInteger size;
+       sq_getinteger(v,2,&size);
+       if(!self->Resize(size))
+               return sq_throwerror(v,_SC("resize failed"));
+       return 0;
+}
+
+static void __swap_dword(unsigned int *n)
+{
+       *n=(unsigned int)(((*n&0xFF000000)>>24)  |
+                       ((*n&0x00FF0000)>>8)  |
+                       ((*n&0x0000FF00)<<8)  |
+                       ((*n&0x000000FF)<<24));
+}
+
+static void __swap_word(unsigned short *n)
+{
+       *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00);
+}
+
+static SQInteger _blob_swap4(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       SQInteger num=(self->Len()-(self->Len()%4))>>2;
+       unsigned int *t=(unsigned int *)self->GetBuf();
+       for(SQInteger i = 0; i < num; i++) {
+               __swap_dword(&t[i]);
+       }
+       return 0;
+}
+
+static SQInteger _blob_swap2(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       SQInteger num=(self->Len()-(self->Len()%2))>>1;
+       unsigned short *t = (unsigned short *)self->GetBuf();
+       for(SQInteger i = 0; i < num; i++) {
+               __swap_word(&t[i]);
+       }
+       return 0;
+}
+
+static SQInteger _blob__set(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       SQInteger idx,val;
+       sq_getinteger(v,2,&idx);
+       sq_getinteger(v,3,&val);
+       if(idx < 0 || idx >= self->Len())
+               return sq_throwerror(v,_SC("index out of range"));
+       ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val;
+       sq_push(v,3);
+       return 1;
+}
+
+static SQInteger _blob__get(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       SQInteger idx;
+       sq_getinteger(v,2,&idx);
+       if(idx < 0 || idx >= self->Len())
+               return sq_throwerror(v,_SC("index out of range"));
+       sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]);
+       return 1;
+}
+
+static SQInteger _blob__nexti(HSQUIRRELVM v)
+{
+       SETUP_BLOB(v);
+       if(sq_gettype(v,2) == OT_NULL) {
+               sq_pushinteger(v, 0);
+               return 1;
+       }
+       SQInteger idx;
+       if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) {
+               if(idx+1 < self->Len()) {
+                       sq_pushinteger(v, idx+1);
+                       return 1;
+               }
+               sq_pushnull(v);
+               return 1;
+       }
+       return sq_throwerror(v,_SC("internal error (_nexti) wrong argument type"));
+}
+
+static SQInteger _blob__typeof(HSQUIRRELVM v)
+{
+       sq_pushstring(v,_SC("blob"),-1);
+       return 1;
+}
+
+static SQInteger _blob_releasehook(SQUserPointer p, SQInteger size)
+{
+       SQBlob *self = (SQBlob*)p;
+       delete self;
+       return 1;
+}
+
+static SQInteger _blob_constructor(HSQUIRRELVM v)
+{
+       SQInteger nparam = sq_gettop(v);
+       SQInteger size = 0;
+       if(nparam == 2) {
+               sq_getinteger(v, 2, &size);
+       }
+       if(size < 0) return sq_throwerror(v, _SC("cannot create blob with negative size"));
+       SQBlob *b = new SQBlob(size);
+       if(SQ_FAILED(sq_setinstanceup(v,1,b))) {
+               delete b;
+               return sq_throwerror(v, _SC("cannot create blob with negative size"));
+       }
+       sq_setreleasehook(v,1,_blob_releasehook);
+       return 0;
+}
+
+#define _DECL_BLOB_FUNC(name,nparams,typecheck) {_SC(#name),_blob_##name,nparams,typecheck}
+static SQRegFunction _blob_methods[] = {
+       _DECL_BLOB_FUNC(constructor,-1,_SC("xn")),
+       _DECL_BLOB_FUNC(resize,2,_SC("xn")),
+       _DECL_BLOB_FUNC(swap2,1,_SC("x")),
+       _DECL_BLOB_FUNC(swap4,1,_SC("x")),
+       _DECL_BLOB_FUNC(_set,3,_SC("xnn")),
+       _DECL_BLOB_FUNC(_get,2,_SC("xn")),
+       _DECL_BLOB_FUNC(_typeof,1,_SC("x")),
+       _DECL_BLOB_FUNC(_nexti,2,_SC("x")),
+       {0,0,0,0}
+};
+
+
+
+//GLOBAL FUNCTIONS
+
+static SQInteger _g_blob_casti2f(HSQUIRRELVM v)
+{
+       SQInteger i;
+       sq_getinteger(v,2,&i);
+       sq_pushfloat(v,*((SQFloat *)&i));
+       return 1;
+}
+
+static SQInteger _g_blob_castf2i(HSQUIRRELVM v)
+{
+       SQFloat f;
+       sq_getfloat(v,2,&f);
+       sq_pushinteger(v,*((SQInteger *)&f));
+       return 1;
+}
+
+static SQInteger _g_blob_swap2(HSQUIRRELVM v)
+{
+       SQInteger i;
+       sq_getinteger(v,2,&i);
+       short s=(short)i;
+       sq_pushinteger(v,(s<<8)|((s>>8)&0x00FF));
+       return 1;
+}
+
+static SQInteger _g_blob_swap4(HSQUIRRELVM v)
+{
+       SQInteger i;
+       sq_getinteger(v,2,&i);
+       unsigned int t4 = (unsigned int)i;
+       __swap_dword(&t4);
+       sq_pushinteger(v,(SQInteger)t4);
+       return 1;
+}
+
+static SQInteger _g_blob_swapfloat(HSQUIRRELVM v)
+{
+       SQFloat f;
+       sq_getfloat(v,2,&f);
+       __swap_dword((unsigned int *)&f);
+       sq_pushfloat(v,f);
+       return 1;
+}
+
+#define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck}
+static SQRegFunction bloblib_funcs[]={
+       _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")),
+       _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")),
+       _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")),
+       _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")),
+       _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")),
+       {0,0}
+};
+
+SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr)
+{
+       SQBlob *blob;
+       if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG)))
+               return -1;
+       *ptr = blob->GetBuf();
+       return SQ_OK;
+}
+
+SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx)
+{
+       SQBlob *blob;
+       if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG)))
+               return -1;
+       return blob->Len();
+}
+
+SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size)
+{
+       SQInteger top = sq_gettop(v);
+       sq_pushregistrytable(v);
+       sq_pushstring(v,_SC("std_blob"),-1);
+       if(SQ_SUCCEEDED(sq_get(v,-2))) {
+               sq_remove(v,-2); //removes the registry
+               sq_push(v,1); // push the this
+               sq_pushinteger(v,size); //size
+               SQBlob *blob = NULL;
+               if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse))
+                       && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) {
+                       sq_remove(v,-2);
+                       return blob->GetBuf();
+               }
+       }
+       sq_settop(v,top);
+       return NULL;
+}
+
+SQRESULT sqstd_register_bloblib(HSQUIRRELVM v)
+{
+       return declare_stream(v,_SC("blob"),(SQUserPointer)SQSTD_BLOB_TYPE_TAG,_SC("std_blob"),_blob_methods,bloblib_funcs);
+}
+
diff --git a/src/squirrel/sqstdlib/sqstdblobimpl.h b/src/squirrel/sqstdlib/sqstdblobimpl.h
new file mode 100644 (file)
index 0000000..9f22c0a
--- /dev/null
@@ -0,0 +1,108 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_BLOBIMPL_H_
+#define _SQSTD_BLOBIMPL_H_
+
+struct SQBlob : public SQStream
+{
+       SQBlob(SQInteger size) {
+               _size = size;
+               _allocated = size;
+               _buf = (unsigned char *)sq_malloc(size);
+               memset(_buf, 0, _size);
+               _ptr = 0;
+               _owns = true;
+       }
+       virtual ~SQBlob() {
+               sq_free(_buf, _allocated);
+       }
+       SQInteger Write(void *buffer, SQInteger size) {
+               if(!CanAdvance(size)) {
+                       GrowBufOf(_ptr + size - _size);
+               }
+               memcpy(&_buf[_ptr], buffer, size);
+               _ptr += size;
+               return size;
+       }
+       SQInteger Read(void *buffer,SQInteger size) {
+               SQInteger n = size;
+               if(!CanAdvance(size)) {
+                       if((_size - _ptr) > 0)
+                               n = _size - _ptr;
+                       else return 0;
+               }
+               memcpy(buffer, &_buf[_ptr], n);
+               _ptr += n;
+               return n;
+       }
+       bool Resize(SQInteger n) {
+               if(!_owns) return false;
+               if(n != _allocated) {
+                       unsigned char *newbuf = (unsigned char *)sq_malloc(n);
+                       memset(newbuf,0,n);
+                       if(_size > n)
+                               memcpy(newbuf,_buf,n);
+                       else
+                               memcpy(newbuf,_buf,_size);
+                       sq_free(_buf,_allocated);
+                       _buf=newbuf;
+                       _allocated = n;
+                       if(_size > _allocated)
+                               _size = _allocated;
+                       if(_ptr > _allocated)
+                               _ptr = _allocated;
+               }
+               return true;
+       }
+       bool GrowBufOf(SQInteger n)
+       {
+               bool ret = true;
+               if(_size + n > _allocated) {
+                       if(_size + n > _size * 2)
+                               ret = Resize(_size + n);
+                       else
+                               ret = Resize(_size * 2);
+               }
+               _size = _size + n;
+               return ret;
+       }
+       bool CanAdvance(SQInteger n) {
+               if(_ptr+n>_size)return false;
+               return true;
+       }
+       SQInteger Seek(SQInteger offset, SQInteger origin) {
+               switch(origin) {
+                       case SQ_SEEK_SET:
+                               if(offset > _size || offset < 0) return -1;
+                               _ptr = offset;
+                               break;
+                       case SQ_SEEK_CUR:
+                               if(_ptr + offset > _size || _ptr + offset < 0) return -1;
+                               _ptr += offset;
+                               break;
+                       case SQ_SEEK_END:
+                               if(_size + offset > _size || _size + offset < 0) return -1;
+                               _ptr = _size + offset;
+                               break;
+                       default: return -1;
+               }
+               return 0;
+       }
+       bool IsValid() {
+               return _buf?true:false;
+       }
+       bool EOS() {
+               return _ptr == _size;
+       }
+       SQInteger Flush() { return 0; }
+       SQInteger Tell() { return _ptr; }
+       SQInteger Len() { return _size; }
+       SQUserPointer GetBuf(){ return _buf; }
+private:
+       SQInteger _size;
+       SQInteger _allocated;
+       SQInteger _ptr;
+       unsigned char *_buf;
+       bool _owns;
+};
+
+#endif //_SQSTD_BLOBIMPL_H_
diff --git a/src/squirrel/sqstdlib/sqstdio.cpp b/src/squirrel/sqstdlib/sqstdio.cpp
new file mode 100644 (file)
index 0000000..81ed732
--- /dev/null
@@ -0,0 +1,410 @@
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <stdio.h>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include "sqstdstream.h"
+
+#define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001)
+//basic API
+SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode)
+{
+#ifndef _UNICODE
+       return (SQFILE)fopen(filename,mode);
+#else
+       return (SQFILE)_wfopen(filename,mode);
+#endif
+}
+
+SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file)
+{
+       return (SQInteger)fread(buffer,size,count,(FILE *)file);
+}
+
+SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
+{
+       return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
+}
+
+SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
+{
+       SQInteger realorigin;
+       switch(origin) {
+               case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
+               case SQ_SEEK_END: realorigin = SEEK_END; break;
+               case SQ_SEEK_SET: realorigin = SEEK_SET; break;
+               default: return -1; //failed
+       }
+       return fseek((FILE *)file,(long)offset,(int)realorigin);
+}
+
+SQInteger sqstd_ftell(SQFILE file)
+{
+       return ftell((FILE *)file);
+}
+
+SQInteger sqstd_fflush(SQFILE file)
+{
+       return fflush((FILE *)file);
+}
+
+SQInteger sqstd_fclose(SQFILE file)
+{
+       return fclose((FILE *)file);
+}
+
+SQInteger sqstd_feof(SQFILE file)
+{
+       return feof((FILE *)file);
+}
+
+//File
+struct SQFile : public SQStream {
+       SQFile() { _handle = NULL; _owns = false;}
+       SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
+       virtual ~SQFile() { Close(); }
+       bool Open(const SQChar *filename ,const SQChar *mode) {
+               Close();
+               if( (_handle = sqstd_fopen(filename,mode)) ) {
+                       _owns = true;
+                       return true;
+               }
+               return false;
+       }
+       void Close() {
+               if(_handle && _owns) { 
+                       sqstd_fclose(_handle);
+                       _handle = NULL;
+                       _owns = false;
+               }
+       }
+       SQInteger Read(void *buffer,SQInteger size) {
+               return sqstd_fread(buffer,1,size,_handle);
+       }
+       SQInteger Write(void *buffer,SQInteger size) {
+               return sqstd_fwrite(buffer,1,size,_handle);
+       }
+       SQInteger Flush() {
+               return sqstd_fflush(_handle);
+       }
+       SQInteger Tell() {
+               return sqstd_ftell(_handle);
+       }
+       SQInteger Len() {
+               SQInteger prevpos=Tell();
+               Seek(0,SQ_SEEK_END);
+               SQInteger size=Tell();
+               Seek(prevpos,SQ_SEEK_SET);
+               return size;
+       }
+       SQInteger Seek(SQInteger offset, SQInteger origin)      {
+               return sqstd_fseek(_handle,offset,origin);
+       }
+       bool IsValid() { return _handle?true:false; }
+       bool EOS() { return Tell()==Len()?true:false;}
+       SQFILE GetHandle() {return _handle;}
+private:
+       SQFILE _handle;
+       bool _owns;
+};
+
+static SQInteger _file__typeof(HSQUIRRELVM v)
+{
+       sq_pushstring(v,_SC("file"),-1);
+       return 1;
+}
+
+static SQInteger _file_releasehook(SQUserPointer p, SQInteger size)
+{
+       SQFile *self = (SQFile*)p;
+       delete self;
+       return 1;
+}
+
+static SQInteger _file_constructor(HSQUIRRELVM v)
+{
+       const SQChar *filename,*mode;
+       bool owns = true;
+       SQFile *f;
+       SQFILE newf;
+       if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {
+               sq_getstring(v, 2, &filename);
+               sq_getstring(v, 3, &mode);
+               newf = sqstd_fopen(filename, mode);
+               if(!newf) return sq_throwerror(v, _SC("cannot open file"));
+       } else if(sq_gettype(v,2) == OT_USERPOINTER) {
+               owns = !(sq_gettype(v,3) == OT_NULL);
+               sq_getuserpointer(v,2,&newf);
+       } else {
+               return sq_throwerror(v,_SC("wrong parameter"));
+       }
+       f = new SQFile(newf,owns);
+       if(SQ_FAILED(sq_setinstanceup(v,1,f))) {
+               delete f;
+               return sq_throwerror(v, _SC("cannot create blob with negative size"));
+       }
+       sq_setreleasehook(v,1,_file_releasehook);
+       return 0;
+}
+
+//bindings
+#define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
+static SQRegFunction _file_methods[] = {
+       _DECL_FILE_FUNC(constructor,3,_SC("x")),
+       _DECL_FILE_FUNC(_typeof,1,_SC("x")),
+       {0,0,0,0},
+};
+
+
+
+SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)
+{
+       SQInteger top = sq_gettop(v);
+       sq_pushregistrytable(v);
+       sq_pushstring(v,_SC("std_file"),-1);
+       if(SQ_SUCCEEDED(sq_get(v,-2))) {
+               sq_remove(v,-2); //removes the registry
+               sq_pushroottable(v); // push the this
+               sq_pushuserpointer(v,file); //file
+               if(own){
+                       sq_pushinteger(v,1); //true
+               }
+               else{
+                       sq_pushnull(v); //false
+               }
+               if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
+                       sq_remove(v,-2);
+                       return SQ_OK;
+               }
+       }
+       sq_settop(v,top);
+       return SQ_OK;
+}
+
+SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
+{
+       SQFile *fileobj = NULL;
+       if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
+               *file = fileobj->GetHandle();
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("not a file"));
+}
+
+
+
+static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
+{
+       SQInteger ret;
+       char c;
+       if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
+               return c;
+       return 0;
+}
+
+static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
+{
+#define READ() \
+       if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
+               return 0;
+
+       static const SQInteger utf8_lengths[16] =
+       {
+               1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
+               0,0,0,0,                /* 1000 to 1011 : not valid */
+               2,2,                    /* 1100, 1101 : 2 bytes */
+               3,                      /* 1110 : 3 bytes */
+               4                       /* 1111 :4 bytes */
+       };
+       static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
+       unsigned char inchar;
+       SQInteger c = 0;
+       READ();
+       c = inchar;
+       //
+       if(c >= 0x80) {
+               SQInteger tmp;
+               SQInteger codelen = utf8_lengths[c>>4];
+               if(codelen == 0) 
+                       return 0;
+                       //"invalid UTF-8 stream";
+               tmp = c&byte_masks[codelen];
+               for(SQInteger n = 0; n < codelen-1; n++) {
+                       tmp<<=6;
+                       READ();
+                       tmp |= inchar & 0x3F;
+               }
+               c = tmp;
+       }
+       return c;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
+{
+       SQInteger ret;
+       wchar_t c;
+       if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
+               return (SQChar)c;
+       return 0;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
+{
+       SQInteger ret;
+       unsigned short c;
+       if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {
+               c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
+               return (SQChar)c;
+       }
+       return 0;
+}
+
+SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
+{
+       SQInteger ret;
+       if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
+       return -1;
+}
+
+SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
+{
+       return sqstd_fwrite(p,1,size,(SQFILE)file);
+}
+
+SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
+{
+       SQFILE file = sqstd_fopen(filename,_SC("rb"));
+       SQInteger ret;
+       unsigned short us;
+       unsigned char uc;
+       SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
+       if(file){
+               ret = sqstd_fread(&us,1,2,file);
+               if(ret != 2) {
+                       //probably an empty file
+                       us = 0;
+               }
+               if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
+                       sqstd_fseek(file,0,SQ_SEEK_SET);
+                       if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
+                               sqstd_fclose(file);
+                               return SQ_OK;
+                       }
+               }
+               else { //SCRIPT
+                       switch(us)
+                       {
+                               //gotta swap the next 2 lines on BIG endian machines
+                               case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
+                               case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
+                               case 0xBBEF: 
+                                       if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { 
+                                               sqstd_fclose(file); 
+                                               return sq_throwerror(v,_SC("io error")); 
+                                       }
+                                       if(uc != 0xBF) { 
+                                               sqstd_fclose(file); 
+                                               return sq_throwerror(v,_SC("Unrecognozed ecoding")); 
+                                       }
+                                       func = _io_file_lexfeed_UTF8;
+                                       break;//UTF-8 ;
+                               default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
+                       }
+
+                       if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){
+                               sqstd_fclose(file);
+                               return SQ_OK;
+                       }
+               }
+               sqstd_fclose(file);
+               return SQ_ERROR;
+       }
+       return sq_throwerror(v,_SC("cannot open the file"));
+}
+
+SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
+{
+       if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
+               sq_push(v,-2);
+               if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
+                       sq_remove(v,retval?-2:-1); //removes the closure
+                       return 1;
+               }
+               sq_pop(v,1); //removes the closure
+       }
+       return SQ_ERROR;
+}
+
+SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
+{
+       SQFILE file = sqstd_fopen(filename,_SC("wb+"));
+       if(!file) return sq_throwerror(v,_SC("cannot open the file"));
+       if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
+               sqstd_fclose(file);
+               return SQ_OK;
+       }
+       sqstd_fclose(file);
+       return SQ_ERROR; //forward the error
+}
+
+SQInteger _g_io_loadfile(HSQUIRRELVM v)
+{
+       const SQChar *filename;
+       SQBool printerror = SQFalse;
+       sq_getstring(v,2,&filename);
+       if(sq_gettop(v) >= 3) {
+               sq_getbool(v,3,&printerror);
+       }
+       if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
+               return 1;
+       return SQ_ERROR; //propagates the error
+}
+
+SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
+{
+       const SQChar *filename;
+       sq_getstring(v,2,&filename);
+       if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
+               return 1;
+       return SQ_ERROR; //propagates the error
+}
+
+SQInteger _g_io_dofile(HSQUIRRELVM v)
+{
+       const SQChar *filename;
+       SQBool printerror = SQFalse;
+       sq_getstring(v,2,&filename);
+       if(sq_gettop(v) >= 3) {
+               sq_getbool(v,3,&printerror);
+       }
+       sq_push(v,1); //repush the this
+       if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
+               return 1;
+       return SQ_ERROR; //propagates the error
+}
+
+#define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
+static SQRegFunction iolib_funcs[]={
+       _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
+       _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
+       _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
+       {0,0}
+};
+
+SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
+{
+       SQInteger top = sq_gettop(v);
+       //create delegate
+       declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
+       sq_pushstring(v,_SC("stdout"),-1);
+       sqstd_createfile(v,stdout,SQFalse);
+       sq_createslot(v,-3);
+       sq_pushstring(v,_SC("stdin"),-1);
+       sqstd_createfile(v,stdin,SQFalse);
+       sq_createslot(v,-3);
+       sq_pushstring(v,_SC("stderr"),-1);
+       sqstd_createfile(v,stderr,SQFalse);
+       sq_createslot(v,-3);
+       sq_settop(v,top);
+       return SQ_OK;
+}
diff --git a/src/squirrel/sqstdlib/sqstdmath.cpp b/src/squirrel/sqstdlib/sqstdmath.cpp
new file mode 100644 (file)
index 0000000..2ad9f02
--- /dev/null
@@ -0,0 +1,105 @@
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sqstdmath.h>
+
+#define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
+       SQFloat f; \
+       sq_getfloat(v,2,&f); \
+       sq_pushfloat(v,(SQFloat)_funcname(f)); \
+       return 1; \
+}
+
+#define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
+       SQFloat p1,p2; \
+       sq_getfloat(v,2,&p1); \
+       sq_getfloat(v,3,&p2); \
+       sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \
+       return 1; \
+}
+
+static SQInteger math_srand(HSQUIRRELVM v)
+{
+       SQInteger i;
+       if(!sq_getinteger(v,2,&i))return sq_throwerror(v,_SC("invalid param"));
+       srand((unsigned int)i);
+       return 0;
+}
+
+static SQInteger math_rand(HSQUIRRELVM v)
+{
+       sq_pushinteger(v,rand());
+       return 1;
+}
+
+static SQInteger math_abs(HSQUIRRELVM v)
+{
+       SQInteger n;
+       sq_getinteger(v,2,&n);
+       sq_pushinteger(v,(SQInteger)abs((int)n)); 
+       return 1; 
+}
+
+SINGLE_ARG_FUNC(sqrt)
+SINGLE_ARG_FUNC(fabs)
+SINGLE_ARG_FUNC(sin)
+SINGLE_ARG_FUNC(cos)
+SINGLE_ARG_FUNC(asin)
+SINGLE_ARG_FUNC(acos)
+SINGLE_ARG_FUNC(log)
+SINGLE_ARG_FUNC(log10)
+SINGLE_ARG_FUNC(tan)
+SINGLE_ARG_FUNC(atan)
+TWO_ARGS_FUNC(atan2)
+TWO_ARGS_FUNC(pow)
+SINGLE_ARG_FUNC(floor)
+SINGLE_ARG_FUNC(ceil)
+SINGLE_ARG_FUNC(exp)
+
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),math_##name,nparams,tycheck}
+static SQRegFunction mathlib_funcs[] = {
+       _DECL_FUNC(sqrt,2,_SC(".n")),
+       _DECL_FUNC(sin,2,_SC(".n")),
+       _DECL_FUNC(cos,2,_SC(".n")),
+       _DECL_FUNC(asin,2,_SC(".n")),
+       _DECL_FUNC(acos,2,_SC(".n")),
+       _DECL_FUNC(log,2,_SC(".n")),
+       _DECL_FUNC(log10,2,_SC(".n")),
+       _DECL_FUNC(tan,2,_SC(".n")),
+       _DECL_FUNC(atan,2,_SC(".n")),
+       _DECL_FUNC(atan2,3,_SC(".nn")),
+       _DECL_FUNC(pow,3,_SC(".nn")),
+       _DECL_FUNC(floor,2,_SC(".n")),
+       _DECL_FUNC(ceil,2,_SC(".n")),
+       _DECL_FUNC(exp,2,_SC(".n")),
+       _DECL_FUNC(srand,2,_SC(".n")),
+       _DECL_FUNC(rand,1,NULL),
+       _DECL_FUNC(fabs,2,_SC(".n")),
+       _DECL_FUNC(abs,2,_SC(".n")),
+       {0,0},
+};
+
+#ifndef M_PI
+#define M_PI (3.14159265358979323846)
+#endif
+
+SQRESULT sqstd_register_mathlib(HSQUIRRELVM v)
+{
+       SQInteger i=0;
+       while(mathlib_funcs[i].name!=0) {
+               sq_pushstring(v,mathlib_funcs[i].name,-1);
+               sq_newclosure(v,mathlib_funcs[i].f,0);
+               sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask);
+               sq_setnativeclosurename(v,-1,mathlib_funcs[i].name);
+               sq_createslot(v,-3);
+               i++;
+       }
+       sq_pushstring(v,_SC("RAND_MAX"),-1);
+       sq_pushinteger(v,RAND_MAX);
+       sq_createslot(v,-3);
+       sq_pushstring(v,_SC("PI"),-1);
+       sq_pushfloat(v,(SQFloat)M_PI);
+       sq_createslot(v,-3);
+       return SQ_OK;
+}
diff --git a/src/squirrel/sqstdlib/sqstdrex.cpp b/src/squirrel/sqstdlib/sqstdrex.cpp
new file mode 100644 (file)
index 0000000..6f8d59c
--- /dev/null
@@ -0,0 +1,637 @@
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <string.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include "sqstdstring.h"
+
+#ifdef _UINCODE
+#define scisprint iswprint
+#else
+#define scisprint isprint
+#endif
+
+#ifdef _DEBUG
+#include <stdio.h>
+
+static const SQChar *g_nnames[] =
+{
+       _SC("NONE"),_SC("OP_GREEDY"),   _SC("OP_OR"),
+       _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),       _SC("OP_CLASS"),
+       _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
+       _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
+};
+
+#endif
+
+#define OP_GREEDY              (MAX_CHAR+1) // * + ? {n}
+#define OP_OR                  (MAX_CHAR+2)
+#define OP_EXPR                        (MAX_CHAR+3) //parentesis ()
+#define OP_NOCAPEXPR   (MAX_CHAR+4) //parentesis (?:)
+#define OP_DOT                 (MAX_CHAR+5)
+#define OP_CLASS               (MAX_CHAR+6)
+#define OP_CCLASS              (MAX_CHAR+7)
+#define OP_NCLASS              (MAX_CHAR+8) //negates class the [^
+#define OP_RANGE               (MAX_CHAR+9)
+#define OP_CHAR                        (MAX_CHAR+10)
+#define OP_EOL                 (MAX_CHAR+11)
+#define OP_BOL                 (MAX_CHAR+12)
+#define OP_WB                  (MAX_CHAR+13)
+
+#define SQREX_SYMBOL_ANY_CHAR ('.')
+#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
+#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
+#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
+#define SQREX_SYMBOL_BRANCH ('|')
+#define SQREX_SYMBOL_END_OF_STRING ('$')
+#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^')
+#define SQREX_SYMBOL_ESCAPE_CHAR ('\\')
+
+
+typedef int SQRexNodeType;
+
+typedef struct tagSQRexNode{
+       SQRexNodeType type;
+       SQInteger left;
+       SQInteger right;
+       SQInteger next;
+}SQRexNode;
+
+struct SQRex{
+       const SQChar *_eol;
+       const SQChar *_bol;
+       const SQChar *_p;
+       SQInteger _first;
+       SQInteger _op;
+       SQRexNode *_nodes;
+       SQInteger _nallocated;
+       SQInteger _nsize;
+       SQInteger _nsubexpr;
+       SQRexMatch *_matches;
+       SQInteger _currsubexp;
+       void *_jmpbuf;
+       const SQChar **_error;
+};
+
+static SQInteger sqstd_rex_list(SQRex *exp);
+
+static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type)
+{
+       SQRexNode n;
+       n.type = type;
+       n.next = n.right = n.left = -1;
+       if(type == OP_EXPR)
+               n.right = exp->_nsubexpr++;
+       if(exp->_nallocated < (exp->_nsize + 1)) {
+               SQInteger oldsize = exp->_nallocated;
+               exp->_nallocated *= 2;
+               exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode));
+       }
+       exp->_nodes[exp->_nsize++] = n;
+       SQInteger newid = exp->_nsize - 1;
+       return (SQInteger)newid;
+}
+
+static void sqstd_rex_error(SQRex *exp,const SQChar *error)
+{
+       if(exp->_error) *exp->_error = error;
+       longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
+}
+
+static void sqstd_rex_expect(SQRex *exp, SQInteger n){
+       if((*exp->_p) != n) 
+               sqstd_rex_error(exp, _SC("expected paren"));
+       exp->_p++;
+}
+
+static SQChar sqstd_rex_escapechar(SQRex *exp)
+{
+       if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){
+               exp->_p++;
+               switch(*exp->_p) {
+               case 'v': exp->_p++; return '\v';
+               case 'n': exp->_p++; return '\n';
+               case 't': exp->_p++; return '\t';
+               case 'r': exp->_p++; return '\r';
+               case 'f': exp->_p++; return '\f';
+               default: return (*exp->_p++);
+               }
+       } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected"));
+       return (*exp->_p++);
+}
+
+static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid)
+{
+       SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS);
+       exp->_nodes[n].left = classid;
+       return n;
+}
+
+static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass)
+{
+       SQChar t;
+       if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) {
+               exp->_p++;
+               switch(*exp->_p) {
+                       case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n');
+                       case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t');
+                       case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r');
+                       case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f');
+                       case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v');
+                       case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
+                       case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
+                       case 'p': case 'P': case 'l': case 'u': 
+                               {
+                               t = *exp->_p; exp->_p++; 
+                               return sqstd_rex_charclass(exp,t);
+                               }
+                       case 'b': 
+                       case 'B':
+                               if(!isclass) {
+                                       SQInteger node = sqstd_rex_newnode(exp,OP_WB);
+                                       exp->_nodes[node].left = *exp->_p;
+                                       exp->_p++; 
+                                       return node;
+                               } //else default
+                       default: 
+                               t = *exp->_p; exp->_p++; 
+                               return sqstd_rex_newnode(exp,t);
+               }
+       }
+       else if(!scisprint(*exp->_p)) {
+               
+               sqstd_rex_error(exp,_SC("letter expected"));
+       }
+       t = *exp->_p; exp->_p++; 
+       return sqstd_rex_newnode(exp,t);
+}
+static SQInteger sqstd_rex_class(SQRex *exp)
+{
+       SQInteger ret = -1;
+       SQInteger first = -1,chain;
+       if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){
+               ret = sqstd_rex_newnode(exp,OP_NCLASS);
+               exp->_p++;
+       }else ret = sqstd_rex_newnode(exp,OP_CLASS);
+       
+       if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class"));
+       chain = ret;
+       while(*exp->_p != ']' && exp->_p != exp->_eol) {
+               if(*exp->_p == '-' && first != -1){ 
+                       SQInteger r;
+                       if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range"));
+                       r = sqstd_rex_newnode(exp,OP_RANGE);
+                       if(first>*exp->_p) sqstd_rex_error(exp,_SC("invalid range"));
+                       if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges"));
+                       exp->_nodes[r].left = exp->_nodes[first].type;
+                       SQInteger t = sqstd_rex_escapechar(exp);
+                       exp->_nodes[r].right = t;
+            exp->_nodes[chain].next = r;
+                       chain = r;
+                       first = -1;
+               }
+               else{
+                       if(first!=-1){
+                               SQInteger c = first;
+                               exp->_nodes[chain].next = c;
+                               chain = c;
+                               first = sqstd_rex_charnode(exp,SQTrue);
+                       }
+                       else{
+                               first = sqstd_rex_charnode(exp,SQTrue);
+                       }
+               }
+       }
+       if(first!=-1){
+               SQInteger c = first;
+               exp->_nodes[chain].next = c;
+               chain = c;
+               first = -1;
+       }
+       /* hack? */
+       exp->_nodes[ret].left = exp->_nodes[ret].next;
+       exp->_nodes[ret].next = -1;
+       return ret;
+}
+
+static SQInteger sqstd_rex_parsenumber(SQRex *exp)
+{
+       SQInteger ret = *exp->_p-'0';
+       SQInteger positions = 10;
+       exp->_p++;
+       while(isdigit(*exp->_p)) {
+               ret = ret*10+(*exp->_p++-'0');
+               if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant"));
+               positions *= 10;
+       };
+       return ret;
+}
+
+static SQInteger sqstd_rex_element(SQRex *exp)
+{
+       SQInteger ret = -1;
+       switch(*exp->_p)
+       {
+       case '(': {
+               SQInteger expr;
+               exp->_p++;
+
+
+               if(*exp->_p =='?') {
+                       exp->_p++;
+                       sqstd_rex_expect(exp,':');
+                       expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR);
+               }
+               else
+                       expr = sqstd_rex_newnode(exp,OP_EXPR);
+               SQInteger newn = sqstd_rex_list(exp);
+               exp->_nodes[expr].left = newn;
+               ret = expr;
+               sqstd_rex_expect(exp,')');
+                         }
+                         break;
+       case '[':
+               exp->_p++;
+               ret = sqstd_rex_class(exp);
+               sqstd_rex_expect(exp,']');
+               break;
+       case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break;
+       case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break;
+       default:
+               ret = sqstd_rex_charnode(exp,SQFalse);
+               break;
+       }
+
+
+       SQInteger op;
+       SQBool isgreedy = SQFalse;
+       unsigned short p0 = 0, p1 = 0;
+       switch(*exp->_p){
+               case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
+               case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
+               case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break;
+               case '{':
+                       exp->_p++;
+                       if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected"));
+                       p0 = (unsigned short)sqstd_rex_parsenumber(exp);
+                       /*******************************/
+                       switch(*exp->_p) {
+               case '}':
+                       p1 = p0; exp->_p++;
+                       break;
+               case ',':
+                       exp->_p++;
+                       p1 = 0xFFFF;
+                       if(isdigit(*exp->_p)){
+                               p1 = (unsigned short)sqstd_rex_parsenumber(exp);
+                       }
+                       sqstd_rex_expect(exp,'}');
+                       break;
+               default:
+                       sqstd_rex_error(exp,_SC(", or } expected"));
+                       }
+                       /*******************************/
+                       isgreedy = SQTrue; 
+                       break;
+
+       }
+       if(isgreedy) {
+               SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY);
+               op = OP_GREEDY;
+               exp->_nodes[nnode].left = ret;
+               exp->_nodes[nnode].right = ((p0)<<16)|p1;
+               ret = nnode;
+       }
+
+       if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
+               SQInteger nnode = sqstd_rex_element(exp);
+               exp->_nodes[ret].next = nnode;
+       }
+
+       return ret;
+}
+
+static SQInteger sqstd_rex_list(SQRex *exp)
+{
+       SQInteger ret=-1,e;
+       if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) {
+               exp->_p++;
+               ret = sqstd_rex_newnode(exp,OP_BOL);
+       }
+       e = sqstd_rex_element(exp);
+       if(ret != -1) {
+               exp->_nodes[ret].next = e;
+       }
+       else ret = e;
+
+       if(*exp->_p == SQREX_SYMBOL_BRANCH) {
+               SQInteger temp,tright;
+               exp->_p++;
+               temp = sqstd_rex_newnode(exp,OP_OR);
+               exp->_nodes[temp].left = ret;
+               tright = sqstd_rex_list(exp);
+               exp->_nodes[temp].right = tright;
+               ret = temp;
+       }
+       return ret;
+}
+
+static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c)
+{
+       switch(cclass) {
+       case 'a': return isalpha(c)?SQTrue:SQFalse;
+       case 'A': return !isalpha(c)?SQTrue:SQFalse;
+       case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse;
+       case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse;
+       case 's': return isspace(c)?SQTrue:SQFalse;
+       case 'S': return !isspace(c)?SQTrue:SQFalse;
+       case 'd': return isdigit(c)?SQTrue:SQFalse;
+       case 'D': return !isdigit(c)?SQTrue:SQFalse;
+       case 'x': return isxdigit(c)?SQTrue:SQFalse;
+       case 'X': return !isxdigit(c)?SQTrue:SQFalse;
+       case 'c': return iscntrl(c)?SQTrue:SQFalse;
+       case 'C': return !iscntrl(c)?SQTrue:SQFalse;
+       case 'p': return ispunct(c)?SQTrue:SQFalse;
+       case 'P': return !ispunct(c)?SQTrue:SQFalse;
+       case 'l': return islower(c)?SQTrue:SQFalse;
+       case 'u': return isupper(c)?SQTrue:SQFalse;
+       }
+       return SQFalse; /*cannot happen*/
+}
+
+static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c)
+{
+       do {
+               switch(node->type) {
+                       case OP_RANGE:
+                               if(c >= node->left && c <= node->right) return SQTrue;
+                               break;
+                       case OP_CCLASS:
+                               if(sqstd_rex_matchcclass(node->left,c)) return SQTrue;
+                               break;
+                       default:
+                               if(c == node->type)return SQTrue;
+               }
+       } while((node->next != -1) && (node = &exp->_nodes[node->next]));
+       return SQFalse;
+}
+
+static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next)
+{
+       
+       SQRexNodeType type = node->type;
+       switch(type) {
+       case OP_GREEDY: {
+               //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
+               SQRexNode *greedystop = NULL;
+               SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
+               const SQChar *s=str, *good = str;
+
+               if(node->next != -1) {
+                       greedystop = &exp->_nodes[node->next];
+               }
+               else {
+                       greedystop = next;
+               }
+
+               while((nmaches == 0xFFFF || nmaches < p1)) {
+
+                       const SQChar *stop;
+                       if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
+                               break;
+                       nmaches++;
+                       good=s;
+                       if(greedystop) {
+                               //checks that 0 matches satisfy the expression(if so skips)
+                               //if not would always stop(for instance if is a '?')
+                               if(greedystop->type != OP_GREEDY ||
+                               (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
+                               {
+                                       SQRexNode *gnext = NULL;
+                                       if(greedystop->next != -1) {
+                                               gnext = &exp->_nodes[greedystop->next];
+                                       }else if(next && next->next != -1){
+                                               gnext = &exp->_nodes[next->next];
+                                       }
+                                       stop = sqstd_rex_matchnode(exp,greedystop,s,gnext);
+                                       if(stop) {
+                                               //if satisfied stop it
+                                               if(p0 == p1 && p0 == nmaches) break;
+                                               else if(nmaches >= p0 && p1 == 0xFFFF) break;
+                                               else if(nmaches >= p0 && nmaches <= p1) break;
+                                       }
+                               }
+                       }
+                       
+                       if(s >= exp->_eol)
+                               break;
+               }
+               if(p0 == p1 && p0 == nmaches) return good;
+               else if(nmaches >= p0 && p1 == 0xFFFF) return good;
+               else if(nmaches >= p0 && nmaches <= p1) return good;
+               return NULL;
+       }
+       case OP_OR: {
+                       const SQChar *asd = str;
+                       SQRexNode *temp=&exp->_nodes[node->left];
+                       while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
+                               if(temp->next != -1)
+                                       temp = &exp->_nodes[temp->next];
+                               else
+                                       return asd;
+                       }
+                       asd = str;
+                       temp = &exp->_nodes[node->right];
+                       while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
+                               if(temp->next != -1)
+                                       temp = &exp->_nodes[temp->next];
+                               else
+                                       return asd;
+                       }
+                       return NULL;
+                       break;
+       }
+       case OP_EXPR:
+       case OP_NOCAPEXPR:{
+                       SQRexNode *n = &exp->_nodes[node->left];
+                       const SQChar *cur = str;
+                       SQInteger capture = -1;
+                       if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
+                               capture = exp->_currsubexp;
+                               exp->_matches[capture].begin = cur;
+                               exp->_currsubexp++;
+                       }
+                       
+                       do {
+                               SQRexNode *subnext = NULL;
+                               if(n->next != -1) {
+                                       subnext = &exp->_nodes[n->next];
+                               }else {
+                                       subnext = next;
+                               }
+                               if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) {
+                                       if(capture != -1){
+                                               exp->_matches[capture].begin = 0;
+                                               exp->_matches[capture].len = 0;
+                                       }
+                                       return NULL;
+                               }
+                       } while((n->next != -1) && (n = &exp->_nodes[n->next]));
+
+                       if(capture != -1) 
+                               exp->_matches[capture].len = cur - exp->_matches[capture].begin;
+                       return cur;
+       }                                
+       case OP_WB:
+               if(str == exp->_bol && !isspace(*str)
+                || (str == exp->_eol && !isspace(*(str-1)))
+                || (!isspace(*str) && isspace(*(str+1)))
+                || (isspace(*str) && !isspace(*(str+1))) ) {
+                       return (node->left == 'b')?str:NULL;
+               }
+               return (node->left == 'b')?NULL:str;
+       case OP_BOL:
+               if(str == exp->_bol) return str;
+               return NULL;
+       case OP_EOL:
+               if(str == exp->_eol) return str;
+               return NULL;
+       case OP_DOT:{
+               *str++;
+                               }
+               return str;
+       case OP_NCLASS:
+       case OP_CLASS:
+               if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) {
+                       *str++;
+                       return str;
+               }
+               return NULL;
+       case OP_CCLASS:
+               if(sqstd_rex_matchcclass(node->left,*str)) {
+                       *str++;
+                       return str;
+               }
+               return NULL;
+       default: /* char */
+               if(*str != node->type) return NULL;
+               *str++;
+               return str;
+       }
+       return NULL;
+}
+
+/* public api */
+SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error)
+{
+       SQRex *exp = (SQRex *)sq_malloc(sizeof(SQRex));
+       exp->_eol = exp->_bol = NULL;
+       exp->_p = pattern;
+       exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar);
+       exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode));
+       exp->_nsize = 0;
+       exp->_matches = 0;
+       exp->_nsubexpr = 0;
+       exp->_first = sqstd_rex_newnode(exp,OP_EXPR);
+       exp->_error = error;
+       exp->_jmpbuf = sq_malloc(sizeof(jmp_buf));
+       if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
+               SQInteger res = sqstd_rex_list(exp);
+               exp->_nodes[exp->_first].left = res;
+               if(*exp->_p!='\0')
+                       sqstd_rex_error(exp,_SC("unexpected character"));
+#ifdef _DEBUG
+               {
+                       SQInteger nsize,i;
+                       SQRexNode *t;
+                       nsize = exp->_nsize;
+                       t = &exp->_nodes[0];
+                       scprintf(_SC("\n"));
+                       for(i = 0;i < nsize; i++) {
+                               if(exp->_nodes[i].type>MAX_CHAR)
+                                       scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
+                               else
+                                       scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
+                               scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
+                       }
+                       scprintf(_SC("\n"));
+               }
+#endif
+               exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch));
+               memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch));
+       }
+       else{
+               sqstd_rex_free(exp);
+               return NULL;
+       }
+       return exp;
+}
+
+void sqstd_rex_free(SQRex *exp)
+{
+       if(exp) {
+               if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode));
+               if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf));
+               if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch));
+               sq_free(exp,sizeof(SQRex));
+       }
+}
+
+SQBool sqstd_rex_match(SQRex* exp,const SQChar* text)
+{
+       const SQChar* res = NULL;
+       exp->_bol = text;
+       exp->_eol = text + scstrlen(text);
+       exp->_currsubexp = 0;
+       res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL);
+       if(res == NULL || res != exp->_eol)
+               return SQFalse;
+       return SQTrue;
+}
+
+SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end)
+{
+       const SQChar *cur = NULL;
+       SQInteger node = exp->_first;
+       if(text_begin >= text_end) return SQFalse;
+       exp->_bol = text_begin;
+       exp->_eol = text_end;
+       do {
+               cur = text_begin;
+               while(node != -1) {
+                       exp->_currsubexp = 0;
+                       cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL);
+                       if(!cur)
+                               break;
+                       node = exp->_nodes[node].next;
+               }
+               *text_begin++;
+       } while(cur == NULL && text_begin != text_end);
+
+       if(cur == NULL)
+               return SQFalse;
+
+       --text_begin;
+
+       if(out_begin) *out_begin = text_begin;
+       if(out_end) *out_end = cur;
+       return SQTrue;
+}
+
+SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end)
+{
+       return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
+}
+
+SQInteger sqstd_rex_getsubexpcount(SQRex* exp)
+{
+       return exp->_nsubexpr;
+}
+
+SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp)
+{
+       if( n<0 || n >= exp->_nsubexpr) return SQFalse;
+       *subexp = exp->_matches[n];
+       return SQTrue;
+}
+
diff --git a/src/squirrel/sqstdlib/sqstdstream.cpp b/src/squirrel/sqstdlib/sqstdstream.cpp
new file mode 100644 (file)
index 0000000..4cd63da
--- /dev/null
@@ -0,0 +1,330 @@
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include <sqstdblob.h>
+#include "sqstdstream.h"
+#include "sqstdblobimpl.h"
+
+#define SETUP_STREAM(v) \
+       SQStream *self = NULL; \
+       if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_STREAM_TYPE_TAG))) \
+               return sq_throwerror(v,_SC("invalid type tag")); \
+       if(!self->IsValid())  \
+               return sq_throwerror(v,_SC("the stream is invalid"));
+
+SQInteger _stream_readblob(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       SQUserPointer data,blobp;
+       SQInteger size,res;
+       sq_getinteger(v,2,&size);
+       if(size > self->Len()) {
+               size = self->Len();
+       }
+       data = sq_getscratchpad(v,size);
+       res = self->Read(data,size);
+       if(res <= 0)
+               return sq_throwerror(v,_SC("no data left to read"));
+       blobp = sqstd_createblob(v,res);
+       memcpy(blobp,data,res);
+       return 1;
+}
+
+#define SAFE_READN(ptr,len) { \
+       if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \
+       }
+SQInteger _stream_readn(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       SQInteger format;
+       sq_getinteger(v, 2, &format);
+       switch(format) {
+       case 'l': {
+               SQInteger i;
+               SAFE_READN(&i, sizeof(i));
+               sq_pushinteger(v, i);
+                         }
+               break;
+       case 'i': {
+               SQInt32 i;
+               SAFE_READN(&i, sizeof(i));
+               sq_pushinteger(v, i);
+                         }
+               break;
+       case 's': {
+               short s;
+               SAFE_READN(&s, sizeof(short));
+               sq_pushinteger(v, s);
+                         }
+               break;
+       case 'w': {
+               unsigned short w;
+               SAFE_READN(&w, sizeof(unsigned short));
+               sq_pushinteger(v, w);
+                         }
+               break;
+       case 'c': {
+               char c;
+               SAFE_READN(&c, sizeof(char));
+               sq_pushinteger(v, c);
+                         }
+               break;
+       case 'b': {
+               unsigned char c;
+               SAFE_READN(&c, sizeof(unsigned char));
+               sq_pushinteger(v, c);
+                         }
+               break;
+       case 'f': {
+               float f;
+               SAFE_READN(&f, sizeof(float));
+               sq_pushfloat(v, f);
+                         }
+               break;
+       case 'd': {
+               double d;
+               SAFE_READN(&d, sizeof(double));
+               sq_pushfloat(v, (SQFloat)d);
+                         }
+               break;
+       default:
+               return sq_throwerror(v, _SC("invalid format"));
+       }
+       return 1;
+}
+
+SQInteger _stream_writeblob(HSQUIRRELVM v)
+{
+       SQUserPointer data;
+       SQInteger size;
+       SETUP_STREAM(v);
+       if(SQ_FAILED(sqstd_getblob(v,2,&data)))
+               return sq_throwerror(v,_SC("invalid parameter"));
+       size = sqstd_getblobsize(v,2);
+       if(self->Write(data,size) != size)
+               return sq_throwerror(v,_SC("io error"));
+       sq_pushinteger(v,size);
+       return 1;
+}
+
+SQInteger _stream_writen(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       SQInteger format, ti;
+       SQFloat tf;
+       sq_getinteger(v, 3, &format);
+       switch(format) {
+       case 'l': {
+               SQInteger i;
+               sq_getinteger(v, 2, &ti);
+               i = ti;
+               self->Write(&i, sizeof(SQInteger));
+                         }
+               break;
+       case 'i': {
+               SQInt32 i;
+               sq_getinteger(v, 2, &ti);
+               i = (SQInt32)ti;
+               self->Write(&i, sizeof(SQInt32));
+                         }
+               break;
+       case 's': {
+               short s;
+               sq_getinteger(v, 2, &ti);
+               s = (short)ti;
+               self->Write(&s, sizeof(short));
+                         }
+               break;
+       case 'w': {
+               unsigned short w;
+               sq_getinteger(v, 2, &ti);
+               w = (unsigned short)ti;
+               self->Write(&w, sizeof(unsigned short));
+                         }
+               break;
+       case 'c': {
+               char c;
+               sq_getinteger(v, 2, &ti);
+               c = (char)ti;
+               self->Write(&c, sizeof(char));
+                                 }
+               break;
+       case 'b': {
+               unsigned char b;
+               sq_getinteger(v, 2, &ti);
+               b = (unsigned char)ti;
+               self->Write(&b, sizeof(unsigned char));
+                         }
+               break;
+       case 'f': {
+               float f;
+               sq_getfloat(v, 2, &tf);
+               f = (float)tf;
+               self->Write(&f, sizeof(float));
+                         }
+               break;
+       case 'd': {
+               double d;
+               sq_getfloat(v, 2, &tf);
+               d = tf;
+               self->Write(&d, sizeof(double));
+                         }
+               break;
+       default:
+               return sq_throwerror(v, _SC("invalid format"));
+       }
+       return 0;
+}
+
+SQInteger _stream_seek(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       SQInteger offset, origin = SQ_SEEK_SET;
+       sq_getinteger(v, 2, &offset);
+       if(sq_gettop(v) > 2) {
+               SQInteger t;
+               sq_getinteger(v, 3, &t);
+               switch(t) {
+                       case 'b': origin = SQ_SEEK_SET; break;
+                       case 'c': origin = SQ_SEEK_CUR; break;
+                       case 'e': origin = SQ_SEEK_END; break;
+                       default: return sq_throwerror(v,_SC("invalid origin"));
+               }
+       }
+       sq_pushinteger(v, self->Seek(offset, origin));
+       return 1;
+}
+
+SQInteger _stream_tell(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       sq_pushinteger(v, self->Tell());
+       return 1;
+}
+
+SQInteger _stream_len(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       sq_pushinteger(v, self->Len());
+       return 1;
+}
+
+SQInteger _stream_flush(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       if(!self->Flush())
+               sq_pushinteger(v, 1);
+       else
+               sq_pushnull(v);
+       return 1;
+}
+
+SQInteger _stream_eos(HSQUIRRELVM v)
+{
+       SETUP_STREAM(v);
+       if(self->EOS())
+               sq_pushinteger(v, 1);
+       else
+               sq_pushnull(v);
+       return 1;
+}
+
+static SQRegFunction _stream_methods[] = {
+       _DECL_STREAM_FUNC(readblob,2,_SC("xn")),
+       _DECL_STREAM_FUNC(readn,2,_SC("xn")),
+       _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")),
+       _DECL_STREAM_FUNC(writen,3,_SC("xnn")),
+       _DECL_STREAM_FUNC(seek,-2,_SC("xnn")),
+       _DECL_STREAM_FUNC(tell,1,_SC("x")),
+       _DECL_STREAM_FUNC(len,1,_SC("x")),
+       _DECL_STREAM_FUNC(eos,1,_SC("x")),
+       _DECL_STREAM_FUNC(flush,1,_SC("x")),
+       {0,0}
+};
+
+void init_streamclass(HSQUIRRELVM v)
+{
+       sq_pushregistrytable(v);
+       sq_pushstring(v,_SC("std_stream"),-1);
+       if(SQ_FAILED(sq_get(v,-2))) {
+               sq_pushstring(v,_SC("std_stream"),-1);
+               sq_newclass(v,SQFalse);
+               sq_settypetag(v,-1,(SQUserPointer)SQSTD_STREAM_TYPE_TAG);
+               SQInteger i = 0;
+               while(_stream_methods[i].name != 0) {
+                       SQRegFunction &f = _stream_methods[i];
+                       sq_pushstring(v,f.name,-1);
+                       sq_newclosure(v,f.f,0);
+                       sq_setparamscheck(v,f.nparamscheck,f.typemask);
+                       sq_createslot(v,-3);
+                       i++;
+               }
+               sq_createslot(v,-3);
+               sq_pushroottable(v);
+               sq_pushstring(v,_SC("stream"),-1);
+               sq_pushstring(v,_SC("std_stream"),-1);
+               sq_get(v,-4);
+               sq_createslot(v,-3);
+               sq_pop(v,1);
+       }
+       else {
+               sq_pop(v,1); //result
+       }
+       sq_pop(v,1);
+}
+
+SQRESULT declare_stream(HSQUIRRELVM v,SQChar* name,SQUserPointer typetag,SQChar* reg_name,SQRegFunction *methods,SQRegFunction *globals)
+{
+       if(sq_gettype(v,-1) != OT_TABLE)
+               return sq_throwerror(v,_SC("table expected"));
+       SQInteger top = sq_gettop(v);
+       //create delegate
+    init_streamclass(v);
+       sq_pushregistrytable(v);
+       sq_pushstring(v,reg_name,-1);
+       sq_pushstring(v,_SC("std_stream"),-1);
+       if(SQ_SUCCEEDED(sq_get(v,-3))) {
+               sq_newclass(v,SQTrue);
+               sq_settypetag(v,-1,typetag);
+               SQInteger i = 0;
+               while(methods[i].name != 0) {
+                       SQRegFunction &f = methods[i];
+                       sq_pushstring(v,f.name,-1);
+                       sq_newclosure(v,f.f,0);
+                       sq_setparamscheck(v,f.nparamscheck,f.typemask);
+                       sq_setnativeclosurename(v,-1,f.name);
+                       sq_createslot(v,-3);
+                       i++;
+               }
+               sq_createslot(v,-3);
+               sq_pop(v,1);
+               
+               i = 0;
+               while(globals[i].name!=0)
+               {
+                       SQRegFunction &f = globals[i];
+                       sq_pushstring(v,f.name,-1);
+                       sq_newclosure(v,f.f,0);
+                       sq_setparamscheck(v,f.nparamscheck,f.typemask);
+                       sq_setnativeclosurename(v,-1,f.name);
+                       sq_createslot(v,-3);
+                       i++;
+               }
+               //register the class in the target table
+               sq_pushstring(v,name,-1);
+               sq_pushregistrytable(v);
+               sq_pushstring(v,reg_name,-1);
+               sq_get(v,-2);
+               sq_remove(v,-2);
+               sq_createslot(v,-3);
+
+               sq_settop(v,top);
+               return SQ_OK;
+       }
+       sq_settop(v,top);
+       return SQ_ERROR;
+}
diff --git a/src/squirrel/sqstdlib/sqstdstream.h b/src/squirrel/sqstdlib/sqstdstream.h
new file mode 100644 (file)
index 0000000..6b5bb9d
--- /dev/null
@@ -0,0 +1,18 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTD_STREAM_H_
+#define _SQSTD_STREAM_H_
+
+SQInteger _stream_readblob(HSQUIRRELVM v);
+SQInteger _stream_readline(HSQUIRRELVM v);
+SQInteger _stream_readn(HSQUIRRELVM v);
+SQInteger _stream_writeblob(HSQUIRRELVM v);
+SQInteger _stream_writen(HSQUIRRELVM v);
+SQInteger _stream_seek(HSQUIRRELVM v);
+SQInteger _stream_tell(HSQUIRRELVM v);
+SQInteger _stream_len(HSQUIRRELVM v);
+SQInteger _stream_eos(HSQUIRRELVM v);
+SQInteger _stream_flush(HSQUIRRELVM v);
+
+#define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck}
+SQRESULT declare_stream(HSQUIRRELVM v,SQChar* name,SQUserPointer typetag,SQChar* reg_name,SQRegFunction *methods,SQRegFunction *globals);
+#endif /*_SQSTD_STREAM_H_*/
diff --git a/src/squirrel/sqstdlib/sqstdstring.cpp b/src/squirrel/sqstdlib/sqstdstring.cpp
new file mode 100644 (file)
index 0000000..31735e1
--- /dev/null
@@ -0,0 +1,350 @@
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <sqstdstring.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+
+#ifdef _UNICODE
+#define scstrchr wcschr
+#define scsnprintf wsnprintf
+#define scatoi _wtoi
+#define scstrtok wcstok
+#else
+#define scstrchr strchr
+#define scsnprintf snprintf
+#define scatoi atoi
+#define scstrtok strtok
+#endif
+#define MAX_FORMAT_LEN 20
+#define MAX_WFORMAT_LEN        3
+#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
+
+static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
+{
+       SQChar swidth[MAX_WFORMAT_LEN];
+       SQInteger wc = 0;
+       SQInteger start = n;
+       fmt[0] = '%';
+       while (scstrchr(_SC("-+ #0"), src[n])) n++;
+       while (scisdigit(src[n])) {
+               swidth[wc] = src[n];
+               n++;
+               wc++;
+               if(wc>=MAX_WFORMAT_LEN)
+                       return sq_throwerror(v,_SC("width format too long"));
+       }
+       swidth[wc] = '\0';
+       if(wc > 0) {
+               width = scatoi(swidth);
+       }
+       else
+               width = 0;
+       if (src[n] == '.') {
+           n++;
+       
+               wc = 0;
+               while (scisdigit(src[n])) {
+                       swidth[wc] = src[n];
+                       n++;
+                       wc++;
+                       if(wc>=MAX_WFORMAT_LEN)
+                               return sq_throwerror(v,_SC("precision format too long"));
+               }
+               swidth[wc] = '\0';
+               if(wc > 0) {
+                       width += scatoi(swidth);
+               }
+       }
+       if (n-start > MAX_FORMAT_LEN )
+               return sq_throwerror(v,_SC("format too long"));
+       memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
+       fmt[(n-start)+2] = '\0';
+       return n;
+}
+
+static SQInteger _string_format(HSQUIRRELVM v)
+{
+       const SQChar *format;
+       SQChar *dest;
+       SQChar fmt[MAX_FORMAT_LEN];
+       sq_getstring(v,2,&format);
+       SQInteger allocated = (sq_getsize(v,2)+1)*sizeof(SQChar);
+       dest = sq_getscratchpad(v,allocated);
+       SQInteger n = 0,i = 0, nparam = 3, w = 0;
+       while(format[n] != '\0') {
+               if(format[n] != '%') {
+                       assert(i < allocated);
+                       dest[i++] = format[n];
+                       n++;
+               }
+               else if(format[n+1] == '%') { //handles %%
+                               dest[i++] = '%';
+                               n += 2; 
+               }
+               else {
+                       n++;
+                       if( nparam > sq_gettop(v) )
+                               return sq_throwerror(v,_SC("not enough paramters for the given format string"));
+                       n = validate_format(v,fmt,format,n,w);
+                       if(n < 0) return -1;
+                       SQInteger addlen = 0;
+                       SQInteger valtype = 0;
+                       const SQChar *ts;
+                       SQInteger ti;
+                       SQFloat tf;
+                       switch(format[n]) {
+                       case 's':
+                               if(SQ_FAILED(sq_getstring(v,nparam,&ts))) 
+                                       return sq_throwerror(v,_SC("string expected for the specified format"));
+                               addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
+                               valtype = 's';
+                               break;
+                       case 'i': case 'd': case 'c':case 'o':  case 'u':  case 'x':  case 'X':
+                               if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) 
+                                       return sq_throwerror(v,_SC("integer expected for the specified format"));
+                               addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
+                               valtype = 'i';
+                               break;
+                       case 'f': case 'g': case 'G': case 'e':  case 'E':
+                               if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) 
+                                       return sq_throwerror(v,_SC("float expected for the specified format"));
+                               addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
+                               valtype = 'f';
+                               break;
+                       default:
+                               return sq_throwerror(v,_SC("invalid format"));
+                       }
+                       n++;
+                       allocated += addlen;
+                       dest = sq_getscratchpad(v,allocated);
+                       switch(valtype) {
+                       case 's': i += scsprintf(&dest[i],fmt,ts); break;
+                       case 'i': i += scsprintf(&dest[i],fmt,ti); break;
+                       case 'f': i += scsprintf(&dest[i],fmt,tf); break;
+                       };
+                       nparam ++;
+               }
+       }
+       sq_pushstring(v,dest,i);
+       return 1;
+}
+
+static void __strip_l(const SQChar *str,const SQChar **start)
+{
+       const SQChar *t = str;
+       while(((*t) != '\0') && scisspace(*t)){ t++; }
+       *start = t;
+}
+
+static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
+{
+       if(len == 0) {
+               *end = str;
+               return;
+       }
+       const SQChar *t = &str[len-1];
+       while(t != str && scisspace(*t)) { t--; }
+       *end = t+1;
+}
+
+static SQInteger _string_strip(HSQUIRRELVM v)
+{
+       const SQChar *str,*start,*end;
+       sq_getstring(v,2,&str);
+       SQInteger len = sq_getsize(v,2);
+       __strip_l(str,&start);
+       __strip_r(str,len,&end);
+       sq_pushstring(v,start,end - start);
+       return 1;
+}
+
+static SQInteger _string_lstrip(HSQUIRRELVM v)
+{
+       const SQChar *str,*start;
+       sq_getstring(v,2,&str);
+       __strip_l(str,&start);
+       sq_pushstring(v,start,-1);
+       return 1;
+}
+
+static SQInteger _string_rstrip(HSQUIRRELVM v)
+{
+       const SQChar *str,*end;
+       sq_getstring(v,2,&str);
+       SQInteger len = sq_getsize(v,2);
+       __strip_r(str,len,&end);
+       sq_pushstring(v,str,end - str);
+       return 1;
+}
+
+static SQInteger _string_split(HSQUIRRELVM v)
+{
+       const SQChar *str,*seps;
+       SQChar *stemp,*tok;
+       sq_getstring(v,2,&str);
+       sq_getstring(v,3,&seps);
+       if(sq_getsize(v,3) == 0) return sq_throwerror(v,_SC("empty separators string"));
+       SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
+       stemp = sq_getscratchpad(v,memsize);
+       memcpy(stemp,str,memsize);
+       tok = scstrtok(stemp,seps);
+       sq_newarray(v,0);
+       while( tok != NULL ) {
+               sq_pushstring(v,tok,-1);
+               sq_arrayappend(v,-2);
+               tok = scstrtok( NULL, seps );
+       }
+       return 1;
+}
+
+#define SETUP_REX(v) \
+       SQRex *self = NULL; \
+       sq_getinstanceup(v,1,(SQUserPointer *)&self,0); 
+
+static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
+{
+       SQRex *self = ((SQRex *)p);
+       sqstd_rex_free(self);
+       return 1;
+}
+
+static SQInteger _regexp_match(HSQUIRRELVM v)
+{
+       SETUP_REX(v);
+       const SQChar *str;
+       sq_getstring(v,2,&str);
+       if(sqstd_rex_match(self,str) == SQTrue)
+       {
+               sq_pushbool(v,SQTrue);
+               return 1;
+       }
+       sq_pushbool(v,SQFalse);
+       return 1;
+}
+
+static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
+{
+       sq_newtable(v);
+       sq_pushstring(v,_SC("begin"),-1);
+       sq_pushinteger(v,begin - str);
+       sq_rawset(v,-3);
+       sq_pushstring(v,_SC("end"),-1);
+       sq_pushinteger(v,end - str);
+       sq_rawset(v,-3);
+}
+
+static SQInteger _regexp_search(HSQUIRRELVM v)
+{
+       SETUP_REX(v);
+       const SQChar *str,*begin,*end;
+       SQInteger start = 0;
+       sq_getstring(v,2,&str);
+       if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
+       if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
+               _addrexmatch(v,str,begin,end);
+               return 1;
+       }
+       return 0;
+}
+
+static SQInteger _regexp_capture(HSQUIRRELVM v)
+{
+       SETUP_REX(v);
+       const SQChar *str,*begin,*end;
+       SQInteger start = 0;
+       sq_getstring(v,2,&str);
+       if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
+       if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
+               SQInteger n = sqstd_rex_getsubexpcount(self);
+               SQRexMatch match;
+               sq_newarray(v,0);
+               for(SQInteger i = 0;i < n; i++) {
+                       sqstd_rex_getsubexp(self,i,&match);
+                       if(match.len > 0)
+                               _addrexmatch(v,str,match.begin,match.begin+match.len);
+                       else
+                               _addrexmatch(v,str,str,str); //empty match
+                       sq_arrayappend(v,-2);
+               }
+               return 1;
+       }
+       return 0;
+}
+
+static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
+{
+       SETUP_REX(v);
+       sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
+       return 1;
+}
+
+static SQInteger _regexp_constructor(HSQUIRRELVM v)
+{
+       const SQChar *error,*pattern;
+       sq_getstring(v,2,&pattern);
+       SQRex *rex = sqstd_rex_compile(pattern,&error);
+       if(!rex) return sq_throwerror(v,error);
+       sq_setinstanceup(v,1,rex);
+       sq_setreleasehook(v,1,_rexobj_releasehook);
+       return 0;
+}
+
+static SQInteger _regexp__typeof(HSQUIRRELVM v)
+{
+       sq_pushstring(v,_SC("regexp"),-1);
+       return 1;
+}
+
+#define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
+static SQRegFunction rexobj_funcs[]={
+       _DECL_REX_FUNC(constructor,2,_SC(".s")),
+       _DECL_REX_FUNC(search,-2,_SC("xsn")),
+       _DECL_REX_FUNC(match,2,_SC("xs")),
+       _DECL_REX_FUNC(capture,-2,_SC("xsn")),
+       _DECL_REX_FUNC(subexpcount,1,_SC("x")),
+       _DECL_REX_FUNC(_typeof,1,_SC("x")),
+       {0,0}
+};
+
+#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
+static SQRegFunction stringlib_funcs[]={
+       _DECL_FUNC(format,-2,_SC(".s")),
+       _DECL_FUNC(strip,2,_SC(".s")),
+       _DECL_FUNC(lstrip,2,_SC(".s")),
+       _DECL_FUNC(rstrip,2,_SC(".s")),
+       _DECL_FUNC(split,3,_SC(".ss")),
+       {0,0}
+};
+
+
+SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
+{
+       sq_pushstring(v,_SC("regexp"),-1);
+       sq_newclass(v,SQFalse);
+       SQInteger i = 0;
+       while(rexobj_funcs[i].name != 0) {
+               SQRegFunction &f = rexobj_funcs[i];
+               sq_pushstring(v,f.name,-1);
+               sq_newclosure(v,f.f,0);
+               sq_setparamscheck(v,f.nparamscheck,f.typemask);
+               sq_setnativeclosurename(v,-1,f.name);
+               sq_createslot(v,-3);
+               i++;
+       }
+       sq_createslot(v,-3);
+
+       i = 0;
+       while(stringlib_funcs[i].name!=0)
+       {
+               sq_pushstring(v,stringlib_funcs[i].name,-1);
+               sq_newclosure(v,stringlib_funcs[i].f,0);
+               sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
+               sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
+               sq_createslot(v,-3);
+               i++;
+       }
+       return 1;
+}
diff --git a/src/squirrel/sqstdlib/sqstdsystem.cpp b/src/squirrel/sqstdlib/sqstdsystem.cpp
new file mode 100644 (file)
index 0000000..7d4200c
--- /dev/null
@@ -0,0 +1,147 @@
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sqstdsystem.h>
+
+#ifdef SQUNICODE
+#include <wchar.h>
+#define scgetenv _wgetenv
+#define scsystem _wsystem
+#define scasctime _wasctime
+#define scremove _wremove
+#define screname _wrename
+#else
+#define scgetenv getenv
+#define scsystem system
+#define scasctime asctime
+#define scremove remove
+#define screname rename
+#endif
+
+static SQInteger _system_getenv(HSQUIRRELVM v)
+{
+       const SQChar *s;
+       if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){
+        sq_pushstring(v,scgetenv(s),-1);
+               return 1;
+       }
+       return 0;
+}
+
+
+static SQInteger _system_system(HSQUIRRELVM v)
+{
+       const SQChar *s;
+       if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){
+               sq_pushinteger(v,scsystem(s));
+               return 1;
+       }
+       return sq_throwerror(v,_SC("wrong param"));
+}
+
+
+static SQInteger _system_clock(HSQUIRRELVM v)
+{
+       sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC);
+       return 1;
+}
+
+static SQInteger _system_time(HSQUIRRELVM v)
+{
+       time_t t;
+       time(&t);
+       sq_pushinteger(v,*((SQInteger *)&t));
+       return 1;
+}
+
+static SQInteger _system_remove(HSQUIRRELVM v)
+{
+       const SQChar *s;
+       sq_getstring(v,2,&s);
+       if(scremove(s)==-1)
+               return sq_throwerror(v,_SC("remove() failed"));
+       return 0;
+}
+
+static SQInteger _system_rename(HSQUIRRELVM v)
+{
+       const SQChar *oldn,*newn;
+       sq_getstring(v,2,&oldn);
+       sq_getstring(v,3,&newn);
+       if(screname(oldn,newn)==-1)
+               return sq_throwerror(v,_SC("rename() failed"));
+       return 0;
+}
+
+static void _set_integer_slot(HSQUIRRELVM v,const SQChar *name,SQInteger val)
+{
+       sq_pushstring(v,name,-1);
+       sq_pushinteger(v,val);
+       sq_rawset(v,-3);
+}
+
+static SQInteger _system_date(HSQUIRRELVM v)
+{
+       time_t t;
+       SQInteger it;
+       SQInteger format = 'l';
+       if(sq_gettop(v) > 1) {
+               sq_getinteger(v,2,&it);
+               t = it;
+               if(sq_gettop(v) > 2) {
+                       sq_getinteger(v,3,(SQInteger*)&format);
+               }
+       }
+       else {
+               time(&t);
+       }
+       tm *date;
+    if(format == 'u')
+               date = gmtime(&t);
+       else
+               date = localtime(&t);
+       if(!date)
+               return sq_throwerror(v,_SC("crt api failure"));
+       sq_newtable(v);
+       _set_integer_slot(v, _SC("sec"), date->tm_sec);
+    _set_integer_slot(v, _SC("min"), date->tm_min);
+    _set_integer_slot(v, _SC("hour"), date->tm_hour);
+    _set_integer_slot(v, _SC("day"), date->tm_mday);
+    _set_integer_slot(v, _SC("month"), date->tm_mon);
+    _set_integer_slot(v, _SC("year"), date->tm_year+1900);
+    _set_integer_slot(v, _SC("wday"), date->tm_wday);
+    _set_integer_slot(v, _SC("yday"), date->tm_yday);
+       return 1;
+}
+
+
+
+#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask}
+static SQRegFunction systemlib_funcs[]={
+       _DECL_FUNC(getenv,2,_SC(".s")),
+       _DECL_FUNC(system,2,_SC(".s")),
+       _DECL_FUNC(clock,1,NULL),
+       _DECL_FUNC(time,1,NULL),
+       _DECL_FUNC(date,-1,_SC(".nn")),
+       _DECL_FUNC(remove,2,_SC(".s")),
+       _DECL_FUNC(rename,3,_SC(".ss")),
+       {0,0}
+};
+
+
+SQInteger sqstd_register_systemlib(HSQUIRRELVM v)
+{
+       SQInteger i=0;
+       while(systemlib_funcs[i].name!=0)
+       {
+               sq_pushstring(v,systemlib_funcs[i].name,-1);
+               sq_newclosure(v,systemlib_funcs[i].f,0);
+               sq_setparamscheck(v,systemlib_funcs[i].nparamscheck,systemlib_funcs[i].typemask);
+               sq_setnativeclosurename(v,-1,systemlib_funcs[i].name);
+               sq_createslot(v,-3);
+               i++;
+       }
+       return 1;
+}
diff --git a/src/squirrel/squirrel/sqapi.cpp b/src/squirrel/squirrel/sqapi.cpp
new file mode 100644 (file)
index 0000000..d6b08a7
--- /dev/null
@@ -0,0 +1,1231 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "squserdata.h"
+#include "sqcompiler.h"
+#include "sqfuncstate.h"
+#include "sqclass.h"
+
+bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o)
+{
+       *o = &stack_get(v,idx);
+       if(type(**o) != type){
+               SQObjectPtr oval = v->PrintObjVal(**o);
+               v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval));
+               return false;
+       }
+       return true;
+}
+
+#define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; }
+
+#define sq_aux_paramscheck(v,count) \
+{ \
+       if(sq_gettop(v) < count){ v->Raise_Error(_SC("not enough params in the stack")); return SQ_ERROR; }\
+}              
+
+SQInteger sq_aux_throwobject(HSQUIRRELVM v,SQObjectPtr &e)
+{
+       v->_lasterror = e;
+       return SQ_ERROR;
+}
+
+SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type)
+{
+       scsprintf(_ss(v)->GetScratchPad(100), _SC("unexpected type %s"), IdType2Name(type));
+       return sq_throwerror(v, _ss(v)->GetScratchPad(-1));
+}
+
+HSQUIRRELVM sq_open(SQInteger initialstacksize)
+{
+       SQSharedState *ss;
+       SQVM *v;
+       sq_new(ss, SQSharedState);
+       ss->Init();
+       v = (SQVM *)SQ_MALLOC(sizeof(SQVM));
+       new (v) SQVM(ss);
+       ss->_root_vm = v;
+       if(v->Init(NULL, initialstacksize)) {
+               return v;
+       } else {
+               sq_delete(v, SQVM);
+               return NULL;
+       }
+       return v;
+}
+
+HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize)
+{
+       SQSharedState *ss;
+       SQVM *v;
+       ss=_ss(friendvm);
+       
+       v= (SQVM *)SQ_MALLOC(sizeof(SQVM));
+       new (v) SQVM(ss);
+       
+       if(v->Init(friendvm, initialstacksize)) {
+               friendvm->Push(v);
+               return v;
+       } else {
+               sq_delete(v, SQVM);
+               return NULL;
+       }
+}
+
+SQInteger sq_getvmstate(HSQUIRRELVM v)
+{
+       if(v->_suspended)
+               return SQ_VMSTATE_SUSPENDED;
+       else { 
+               if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING;
+               else return SQ_VMSTATE_IDLE;
+       }
+}
+
+void sq_seterrorhandler(HSQUIRRELVM v)
+{
+       SQObject o = stack_get(v, -1);
+       if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) {
+               v->_errorhandler = o;
+               v->Pop();
+       }
+}
+
+void sq_setdebughook(HSQUIRRELVM v)
+{
+       SQObject o = stack_get(v,-1);
+       if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) {
+               v->_debughook = o;
+               v->Pop();
+       }
+}
+
+void sq_close(HSQUIRRELVM v)
+{
+       SQSharedState *ss = _ss(v);
+       _thread(ss->_root_vm)->Finalize();
+       sq_delete(ss, SQSharedState);
+}
+
+SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror)
+{
+       SQObjectPtr o;
+       if(Compile(v, read, p, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) {
+               v->Push(SQClosure::Create(_ss(v), _funcproto(o)));
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable)
+{
+       _ss(v)->_debuginfo = enable?true:false;
+}
+
+void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable)
+{
+       _ss(v)->_notifyallexceptions = enable?true:false;
+}
+
+void sq_addref(HSQUIRRELVM v,HSQOBJECT *po)
+{
+       if(!ISREFCOUNTED(type(*po))) return;
+#ifdef NO_GARBAGE_COLLECTOR
+       __AddRef(po->_type,po->_unVal);
+#else
+       _ss(v)->_refs_table.AddRef(*po);
+#endif
+}
+
+SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po)
+{
+       if(!ISREFCOUNTED(type(*po))) return SQTrue;
+#ifdef NO_GARBAGE_COLLECTOR
+       __Release(po->_type,po->_unVal);
+       return SQFalse; //the ret val doesn't work(and cannot be fixed)
+#else
+       return _ss(v)->_refs_table.Release(*po);
+#endif
+}
+
+const SQChar *sq_objtostring(HSQOBJECT *o) 
+{
+       if(sq_type(*o) == OT_STRING) {
+               return _stringval(*o);
+       }
+       return NULL;
+}
+
+SQInteger sq_objtointeger(HSQOBJECT *o) 
+{
+       if(sq_isnumeric(*o)) {
+               return tointeger(*o);
+       }
+       return 0;
+}
+
+SQFloat sq_objtofloat(HSQOBJECT *o) 
+{
+       if(sq_isnumeric(*o)) {
+               return tofloat(*o);
+       }
+       return 0;
+}
+
+SQBool sq_objtobool(HSQOBJECT *o) 
+{
+       if(sq_isbool(*o)) {
+               return _integer(*o);
+       }
+       return SQFalse;
+}
+
+void sq_pushnull(HSQUIRRELVM v)
+{
+       v->Push(_null_);
+}
+
+void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len)
+{
+       if(s)
+               v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len)));
+       else v->Push(_null_);
+}
+
+void sq_pushinteger(HSQUIRRELVM v,SQInteger n)
+{
+       v->Push(n);
+}
+
+void sq_pushbool(HSQUIRRELVM v,SQBool b)
+{
+       v->Push(b?true:false);
+}
+
+void sq_pushfloat(HSQUIRRELVM v,SQFloat n)
+{
+       v->Push(n);
+}
+
+void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p)
+{
+       v->Push(p);
+}
+
+SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size)
+{
+       SQUserData *ud = SQUserData::Create(_ss(v), size);
+       v->Push(ud);
+       return ud->_val;
+}
+
+void sq_newtable(HSQUIRRELVM v)
+{
+       v->Push(SQTable::Create(_ss(v), 0));    
+}
+
+void sq_newarray(HSQUIRRELVM v,SQInteger size)
+{
+       v->Push(SQArray::Create(_ss(v), size)); 
+}
+
+SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase)
+{
+       SQClass *baseclass = NULL;
+       if(hasbase) {
+               SQObjectPtr &base = stack_get(v,-1);
+               if(type(base) != OT_CLASS)
+                       return sq_throwerror(v,_SC("invalid base type"));
+               baseclass = _class(base);
+       }
+       SQClass *newclass = SQClass::Create(_ss(v), baseclass);
+       if(baseclass) v->Pop();
+       v->Push(newclass);      
+       return SQ_OK;
+}
+
+SQBool sq_instanceof(HSQUIRRELVM v)
+{
+       SQObjectPtr &inst = stack_get(v,-1);
+       SQObjectPtr &cl = stack_get(v,-2);
+       if(type(inst) != OT_INSTANCE || type(cl) != OT_CLASS)
+               return sq_throwerror(v,_SC("invalid param type"));
+       return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse;
+}
+
+SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx)
+{
+       sq_aux_paramscheck(v,2);
+       SQObjectPtr *arr;
+       _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+       _array(*arr)->Append(v->GetUp(-1));
+       v->Pop(1);
+       return SQ_OK;
+}
+
+SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+       sq_aux_paramscheck(v, 1);
+       SQObjectPtr *arr;
+       _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+       if(_array(*arr)->Size() > 0) {
+        if(pushval != 0){ v->Push(_array(*arr)->Top()); }
+               _array(*arr)->Pop();
+               return SQ_OK;
+       }
+       return sq_throwerror(v, _SC("empty array"));
+}
+
+SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize)
+{
+       sq_aux_paramscheck(v,1);
+       SQObjectPtr *arr;
+       _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+       if(_array(*arr)->Size() > 0) {
+               _array(*arr)->Resize(newsize);
+               return SQ_OK;
+       }
+       return SQ_OK;
+}
+
+
+SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx)
+{
+       sq_aux_paramscheck(v, 1);
+       SQObjectPtr *o;
+       _GETSAFE_OBJ(v, idx, OT_ARRAY,o);
+       SQArray *arr = _array(*o);
+       if(arr->Size() > 0) {
+               SQObjectPtr t;
+               SQInteger size = arr->Size();
+               SQInteger n = size >> 1; size -= 1;
+               for(SQInteger i = 0; i < n; i++) {
+                       t = arr->_values[i];
+                       arr->_values[i] = arr->_values[size-i];
+                       arr->_values[size-i] = t;
+               }
+               return SQ_OK;
+       }
+       return SQ_OK;
+}
+
+void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars)
+{
+       SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func);
+       nc->_nparamscheck = 0;
+       for(SQUnsignedInteger i = 0; i < nfreevars; i++) {
+               nc->_outervalues.push_back(v->Top());
+               v->Pop();
+       }
+       v->Push(SQObjectPtr(nc));       
+}
+
+SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars)
+{
+       SQObject o = stack_get(v, idx);
+       if(sq_isclosure(o)) {
+               SQClosure *c = _closure(o);
+               SQFunctionProto *proto = _funcproto(c->_function);
+               *nparams = (SQUnsignedInteger)proto->_nparameters;
+        *nfreevars = (SQUnsignedInteger)c->_outervalues.size();
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("the object is not a closure"));
+}
+
+SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name)
+{
+       SQObject o = stack_get(v, idx);
+       if(sq_isnativeclosure(o)) {
+               SQNativeClosure *nc = _nativeclosure(o);
+               nc->_name = SQString::Create(_ss(v),name);
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("the object is not a nativeclosure"));
+}
+
+SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask)
+{
+       SQObject o = stack_get(v, -1);
+       if(!sq_isnativeclosure(o))
+               return sq_throwerror(v, _SC("native closure expected"));
+       SQNativeClosure *nc = _nativeclosure(o);
+       nc->_nparamscheck = nparamscheck;
+       if(typemask) {
+               SQIntVec res;
+               if(!CompileTypemask(res, typemask))
+                       return sq_throwerror(v, _SC("invalid typemask"));
+               nc->_typecheck.copy(res);
+       }
+       else {
+               nc->_typecheck.resize(0);
+       }
+       if(nparamscheck == SQ_MATCHTYPEMASKSTRING) {
+               nc->_nparamscheck = nc->_typecheck.size();
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(!sq_isnativeclosure(o) &&
+               !sq_isclosure(o))
+               return sq_throwerror(v,_SC("the target is not a closure"));
+    SQObjectPtr &env = stack_get(v,-1);
+       if(!sq_istable(env) &&
+               !sq_isclass(env) &&
+               !sq_isinstance(env))
+               return sq_throwerror(v,_SC("invalid environment"));
+       SQObjectPtr w = _refcounted(env)->GetWeakRef(type(env));
+       SQObjectPtr ret;
+       if(sq_isclosure(o)) {
+               SQClosure *c = _closure(o)->Clone();
+               c->_env = w;
+               ret = c;
+       }
+       else { //then must be a native closure
+               SQNativeClosure *c = _nativeclosure(o)->Clone();
+               c->_env = w;
+               ret = c;
+       }
+       v->Pop();
+       v->Push(ret);
+       return SQ_OK;
+}
+
+SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObject &o=stack_get(v,idx);
+       switch(type(o)) {
+               case OT_TABLE: _table(o)->Clear();      break;
+               case OT_ARRAY: _array(o)->Resize(0); break;
+               default:
+                       return sq_throwerror(v, _SC("clear only works on table and array"));
+               break;
+
+       }
+       return SQ_OK;
+}
+
+void sq_pushroottable(HSQUIRRELVM v)
+{
+       v->Push(v->_roottable);
+}
+
+void sq_pushregistrytable(HSQUIRRELVM v)
+{
+       v->Push(_ss(v)->_registry);
+}
+
+SQRESULT sq_setroottable(HSQUIRRELVM v)
+{
+       SQObject o = stack_get(v, -1);
+       if(sq_istable(o) || sq_isnull(o)) {
+               v->_roottable = o;
+               v->Pop();
+               return SQ_OK;
+       }
+       return sq_throwerror(v, _SC("ivalid type"));
+}
+
+void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p)
+{
+       v->_foreignptr = p;
+}
+
+SQUserPointer sq_getforeignptr(HSQUIRRELVM v)
+{
+       return v->_foreignptr;
+}
+
+void sq_push(HSQUIRRELVM v,SQInteger idx)
+{
+       v->Push(stack_get(v, idx));
+}
+
+SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx)
+{
+       return type(stack_get(v, idx));
+}
+
+
+void sq_tostring(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       SQObjectPtr res;
+       v->ToString(o,res);
+       v->Push(res);
+}
+
+void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       *b = v->IsFalse(o)?SQFalse:SQTrue;
+}
+
+SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       if(sq_isnumeric(o)) {
+               *i = tointeger(o);
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       if(sq_isnumeric(o)) {
+               *f = tofloat(o);
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       if(sq_isbool(o)) {
+               *b = _integer(o);
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_STRING,o);
+       *c = _stringval(*o);
+       return SQ_OK;
+}
+
+SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_THREAD,o);
+       *thread = _thread(*o);
+       return SQ_OK;
+}
+
+SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       v->Push(_null_);
+       if(!v->Clone(o, stack_get(v, -1))){
+               v->Pop();
+               return sq_aux_invalidtype(v, type(o));
+       }
+       return SQ_OK;
+}
+
+SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx)
+{
+       SQObjectPtr &o = stack_get(v, idx);
+       SQObjectType type = type(o);
+       switch(type) {
+       case OT_STRING:         return _string(o)->_len;
+       case OT_TABLE:          return _table(o)->CountUsed();
+       case OT_ARRAY:          return _array(o)->Size();
+       case OT_USERDATA:       return _userdata(o)->_size;
+       default:
+               return sq_aux_invalidtype(v, type);
+       }
+}
+
+SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_USERDATA,o);
+       (*p) = _userdataval(*o);
+       if(typetag) *typetag = _userdata(*o)->_typetag;
+       return SQ_OK;
+}
+
+SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       switch(type(o)) {
+               case OT_USERDATA:       _userdata(o)->_typetag = typetag;       break;
+               case OT_CLASS:          _class(o)->_typetag = typetag;          break;
+               default:                        return sq_throwerror(v,_SC("invalid object type"));
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_getobjtypetag(HSQOBJECT *o,SQUserPointer * typetag)
+{
+  switch(type(*o)) {
+    case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break;
+    case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break;
+    case OT_CLASS:    *typetag = _class(*o)->_typetag; break;
+    default: return SQ_ERROR;
+  }
+  return SQ_OK;
+}
+
+SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(SQ_FAILED(sq_getobjtypetag(&o,typetag)))
+               return sq_throwerror(v,_SC("invalid object type"));
+       return SQ_OK;
+}
+
+SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o);
+       (*p) = _userpointer(*o);
+       return SQ_OK;
+}
+
+SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance"));
+       _instance(o)->_userpointer = p;
+       return SQ_OK;
+}
+
+SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class"));
+       if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked"));
+       _class(o)->_udsize = udsize;
+       return SQ_OK;
+}
+
+
+SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance"));
+       (*p) = _instance(o)->_userpointer;
+       if(typetag != 0) {
+               SQClass *cl = _instance(o)->_class;
+               do{
+                       if(cl->_typetag == typetag)
+                               return SQ_OK;
+                       cl = cl->_base;
+               }while(cl != NULL);
+               return sq_throwerror(v,_SC("invalid type tag"));
+       }
+       return SQ_OK;
+}
+
+SQInteger sq_gettop(HSQUIRRELVM v)
+{
+       return (v->_top) - v->_stackbase;
+}
+
+void sq_settop(HSQUIRRELVM v, SQInteger newtop)
+{
+       SQInteger top = sq_gettop(v);
+       if(top > newtop)
+               sq_pop(v, top - newtop);
+       else
+               while(top < newtop) sq_pushnull(v);
+}
+
+void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop)
+{
+       assert(v->_top >= nelemstopop);
+       v->Pop(nelemstopop);
+}
+
+void sq_poptop(HSQUIRRELVM v)
+{
+       assert(v->_top >= 1);
+    v->Pop();
+}
+
+
+void sq_remove(HSQUIRRELVM v, SQInteger idx)
+{
+       v->Remove(idx);
+}
+
+SQInteger sq_cmp(HSQUIRRELVM v)
+{
+       SQInteger res;
+       v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res);
+       return res;
+}
+
+SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic)
+{
+       sq_aux_paramscheck(v, 3);
+       SQObjectPtr &self = stack_get(v, idx);
+       if(type(self) == OT_TABLE || type(self) == OT_CLASS) {
+               SQObjectPtr &key = v->GetUp(-2);
+               if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key"));
+               v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false);
+               v->Pop(2);
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+       sq_aux_paramscheck(v, 2);
+       SQObjectPtr *self;
+       _GETSAFE_OBJ(v, idx, OT_TABLE,self);
+       SQObjectPtr &key = v->GetUp(-1);
+       if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key"));
+       SQObjectPtr res;
+       if(!v->DeleteSlot(*self, key, res)){
+               return SQ_ERROR;
+       }
+       if(pushval)     v->GetUp(-1) = res;
+       else v->Pop(1);
+       return SQ_OK;
+}
+
+SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self = stack_get(v, idx);
+       if(v->Set(self, v->GetUp(-2), v->GetUp(-1),false)) {
+               v->Pop(2);
+               return SQ_OK;
+       }
+       v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR;
+}
+
+SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self = stack_get(v, idx);
+       if(type(v->GetUp(-2)) == OT_NULL) return sq_throwerror(v, _SC("null key"));
+       switch(type(self)) {
+       case OT_TABLE:
+               _table(self)->NewSlot(v->GetUp(-2), v->GetUp(-1));
+               v->Pop(2);
+               return SQ_OK;
+       break;
+       case OT_CLASS:
+               _class(self)->NewSlot(_ss(v), v->GetUp(-2), v->GetUp(-1),false);
+               v->Pop(2);
+               return SQ_OK;
+       break;
+       case OT_INSTANCE:
+               if(_instance(self)->Set(v->GetUp(-2), v->GetUp(-1))) {
+                       v->Pop(2);
+                       return SQ_OK;
+               }
+       break;
+       case OT_ARRAY:
+               if(v->Set(self, v->GetUp(-2), v->GetUp(-1),false)) {
+                       v->Pop(2);
+                       return SQ_OK;
+               }
+       break;
+       default:
+               v->Pop(2);
+               return sq_throwerror(v, _SC("rawset works only on array/table/class and instance"));
+       }
+       v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR;
+}
+
+SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self = stack_get(v, idx);
+       SQObjectPtr &mt = v->GetUp(-1);
+       SQObjectType type = type(self);
+       switch(type) {
+       case OT_TABLE:
+               if(type(mt) == OT_TABLE) {
+                       if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, _SC("delagate cycle")); v->Pop();}
+               else if(type(mt)==OT_NULL) {
+                       _table(self)->SetDelegate(NULL); v->Pop(); }
+               else return sq_aux_invalidtype(v,type);
+               break;
+       case OT_USERDATA:
+               if(type(mt)==OT_TABLE) {
+                       _userdata(self)->SetDelegate(_table(mt)); v->Pop(); }
+               else if(type(mt)==OT_NULL) {
+                       _userdata(self)->SetDelegate(NULL); v->Pop(); }
+               else return sq_aux_invalidtype(v, type);
+               break;
+       default:
+                       return sq_aux_invalidtype(v, type);
+               break;
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+       sq_aux_paramscheck(v, 2);
+       SQObjectPtr *self;
+       _GETSAFE_OBJ(v, idx, OT_TABLE,self);
+       SQObjectPtr &key = v->GetUp(-1);
+       SQObjectPtr t;
+       if(_table(*self)->Get(key,t)) {
+               _table(*self)->Remove(key);
+       }
+       if(pushval != 0)
+               if(pushval)     v->GetUp(-1) = t;
+       else
+               v->Pop(1);
+       return SQ_OK;
+}
+
+SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self=stack_get(v,idx);
+       switch(type(self)){
+       case OT_TABLE:
+       case OT_USERDATA:
+               if(!_delegable(self)->_delegate){
+                       v->Push(_null_);
+                       break;
+               }
+               v->Push(SQObjectPtr(_delegable(self)->_delegate));
+               break;
+       default: return sq_throwerror(v,_SC("wrong type")); break;
+       }
+       return SQ_OK;
+       
+}
+
+SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self=stack_get(v,idx);
+       if(v->Get(self,v->GetUp(-1),v->GetUp(-1),false,false))
+               return SQ_OK;
+       v->Pop(1);
+       return sq_throwerror(v,_SC("the index doesn't exist"));
+}
+
+SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &self=stack_get(v,idx);
+       switch(type(self)) {
+       case OT_TABLE:
+               if(_table(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+                       return SQ_OK;
+               break;
+       case OT_CLASS:
+               if(_class(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+                       return SQ_OK;
+               break;
+       case OT_INSTANCE:
+               if(_instance(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+                       return SQ_OK;
+               break;
+       case OT_ARRAY:
+               if(v->Get(self,v->GetUp(-1),v->GetUp(-1),false,false))
+                       return SQ_OK;
+               break;
+       default:
+               v->Pop(1);
+               return sq_throwerror(v,_SC("rawget works only on array/table/instance and class"));
+       }       
+       v->Pop(1);
+       return sq_throwerror(v,_SC("the index doesn't exist"));
+}
+
+SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po)
+{
+       *po=stack_get(v,idx);
+       return SQ_OK;
+}
+
+const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx)
+{
+       SQUnsignedInteger cstksize=v->_callsstacksize;
+       SQUnsignedInteger lvl=(cstksize-level)-1;
+       SQInteger stackbase=v->_stackbase;
+       if(lvl<cstksize){
+               for(SQUnsignedInteger i=0;i<level;i++){
+                       SQVM::CallInfo &ci=v->_callsstack[(cstksize-i)-1];
+                       stackbase-=ci._prevstkbase;
+               }
+               SQVM::CallInfo &ci=v->_callsstack[lvl];
+               if(type(ci._closure)!=OT_CLOSURE)
+                       return NULL;
+               SQClosure *c=_closure(ci._closure);
+               SQFunctionProto *func=_funcproto(c->_function);
+               if(func->_noutervalues > (SQInteger)idx) {
+                       v->Push(c->_outervalues[idx]);
+                       return _stringval(func->_outervalues[idx]._name);
+               }
+               idx -= func->_noutervalues;
+               return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1);
+       }
+       return NULL;
+}
+
+void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
+{
+       v->Push(SQObjectPtr(obj));
+}
+
+void sq_resetobject(HSQOBJECT *po)
+{
+       po->_unVal.pUserPointer=NULL;po->_type=OT_NULL;
+}
+
+SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err)
+{
+       v->_lasterror=SQString::Create(_ss(v),err);
+       return -1;
+}
+
+void sq_reseterror(HSQUIRRELVM v)
+{
+       v->_lasterror = _null_;
+}
+
+void sq_getlasterror(HSQUIRRELVM v)
+{
+       v->Push(v->_lasterror);
+}
+
+void sq_reservestack(HSQUIRRELVM v,SQInteger nsize)
+{
+       if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) {
+               v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size()));
+       }
+}
+
+SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror)
+{
+       if(type(v->GetUp(-1))==OT_GENERATOR){
+               v->Push(_null_); //retval
+               if(!v->Execute(v->GetUp(-2),v->_top,0,v->_top,v->GetUp(-1),raiseerror,SQVM::ET_RESUME_GENERATOR))
+               {v->Raise_Error(v->_lasterror); return SQ_ERROR;}
+               if(!retval)
+                       v->Pop();
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("only generators can be resumed"));
+}
+
+SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror)
+{
+       SQObjectPtr res;
+       if(v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){
+               v->Pop(params);//pop closure and args
+               if(retval){
+                       v->Push(res); return SQ_OK;
+               }
+               return SQ_OK;
+       }
+       else {
+               v->Pop(params);
+               return SQ_ERROR;
+       }
+       if(!v->_suspended)
+               v->Pop(params);
+       return sq_throwerror(v,_SC("call failed"));
+}
+
+SQRESULT sq_suspendvm(HSQUIRRELVM v)
+{
+       return v->Suspend();
+}
+
+SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseerror)
+{
+       SQObjectPtr ret;
+       if(!v->_suspended)
+               return sq_throwerror(v,_SC("cannot resume a vm that is not running any code"));
+       if(wakeupret) {
+               v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval
+               v->Pop();
+       } else v->GetAt(v->_stackbase+v->_suspended_target)=_null_;
+       if(!v->Execute(_null_,v->_top,-1,-1,ret,raiseerror,SQVM::ET_RESUME_VM))
+               return SQ_ERROR;
+       if(sq_getvmstate(v) == SQ_VMSTATE_IDLE) {
+               while (v->_top > 1) v->_stack[--v->_top] = _null_;
+       }
+       if(retval)
+               v->Push(ret);
+       return SQ_OK;
+}
+
+void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook)
+{
+       if(sq_gettop(v) >= 1){
+               SQObjectPtr &ud=stack_get(v,idx);
+               switch( type(ud) ) {
+               case OT_USERDATA:       _userdata(ud)->_hook = hook;    break;
+               case OT_INSTANCE:       _instance(ud)->_hook = hook;    break;
+               case OT_CLASS:          _class(ud)->_hook = hook;               break;
+               default: break; //shutup compiler
+               }
+       }
+}
+
+void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f)
+{
+       _ss(v)->_compilererrorhandler = f;
+}
+
+SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, -1, OT_CLOSURE,o);
+       unsigned short tag = SQ_BYTECODE_STREAM_TAG;
+       if(w(up,&tag,2) != 2)
+               return sq_throwerror(v,_SC("io error"));
+       if(!_closure(*o)->Save(v,up,w))
+               return SQ_ERROR;
+       return SQ_OK;
+}
+
+SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up)
+{
+       SQObjectPtr closure;
+       
+       unsigned short tag;
+       if(r(up,&tag,2) != 2)
+               return sq_throwerror(v,_SC("io error"));
+       if(tag != SQ_BYTECODE_STREAM_TAG)
+               return sq_throwerror(v,_SC("invalid stream"));
+       if(!SQClosure::Load(v,up,r,closure))
+               return SQ_ERROR;
+       v->Push(closure);
+       return SQ_OK;
+}
+
+SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
+{
+       return _ss(v)->GetScratchPad(minsize);
+}
+
+SQInteger sq_collectgarbage(HSQUIRRELVM v)
+{
+#ifndef NO_GARBAGE_COLLECTOR
+       return _ss(v)->CollectGarbage(v);
+#else
+       return -1;
+#endif
+}
+
+const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
+{
+       SQObjectPtr &self = stack_get(v,idx);
+       const SQChar *name = NULL;
+       if(type(self) == OT_CLOSURE) {
+               if(_closure(self)->_outervalues.size()>nval) {
+                       v->Push(_closure(self)->_outervalues[nval]);
+                       SQFunctionProto *fp = _funcproto(_closure(self)->_function);
+                       SQOuterVar &ov = fp->_outervalues[nval];
+                       name = _stringval(ov._name);
+               }
+       }
+       return name;
+}
+
+SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
+{
+       SQObjectPtr &self=stack_get(v,idx);
+       switch(type(self))
+       {
+       case OT_CLOSURE:
+               if(_closure(self)->_outervalues.size()>nval){
+                       _closure(self)->_outervalues[nval]=stack_get(v,-1);
+               }
+               else return sq_throwerror(v,_SC("invalid free var index"));
+               break;
+       case OT_NATIVECLOSURE:
+               if(_nativeclosure(self)->_outervalues.size()>nval){
+                       _nativeclosure(self)->_outervalues[nval]=stack_get(v,-1);
+               }
+               else return sq_throwerror(v,_SC("invalid free var index"));
+               break;
+       default:
+               return sq_aux_invalidtype(v,type(self));
+       }
+       v->Pop(1);
+       return SQ_OK;
+}
+
+SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+       SQObjectPtr &key = stack_get(v,-2);
+       SQObjectPtr &val = stack_get(v,-1);
+       SQObjectPtr attrs;
+       if(type(key) == OT_NULL) {
+               attrs = _class(*o)->_attributes;
+               _class(*o)->_attributes = val;
+               v->Pop(2);
+               v->Push(attrs);
+               return SQ_OK;
+       }else if(_class(*o)->GetAttributes(key,attrs)) {
+               _class(*o)->SetAttributes(key,val);
+               v->Pop(2);
+               v->Push(attrs);
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("wrong index"));
+}
+
+SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+       SQObjectPtr &key = stack_get(v,-1);
+       SQObjectPtr attrs;
+       if(type(key) == OT_NULL) {
+               attrs = _class(*o)->_attributes;
+               v->Pop();
+               v->Push(attrs); 
+               return SQ_OK;
+       }
+       else if(_class(*o)->GetAttributes(key,attrs)) {
+               v->Pop();
+               v->Push(attrs);
+               return SQ_OK;
+       }
+       return sq_throwerror(v,_SC("wrong index"));
+}
+
+SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+       if(_class(*o)->_base)
+               v->Push(SQObjectPtr(_class(*o)->_base));
+       else
+               v->Push(_null_);
+       return SQ_OK;
+}
+
+SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_INSTANCE,o);
+       v->Push(SQObjectPtr(_instance(*o)->_class));
+       return SQ_OK;
+}
+
+SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr *o = NULL;
+       _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+       v->Push(_class(*o)->CreateInstance());
+       return SQ_OK;
+}
+
+void sq_weakref(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObject &o=stack_get(v,idx);
+       if(ISREFCOUNTED(type(o))) {
+               v->Push(_refcounted(o)->GetWeakRef(type(o)));
+               return;
+       }
+       v->Push(o);
+}
+
+SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr &o = stack_get(v,idx);
+       if(type(o) != OT_WEAKREF) {
+               return sq_throwerror(v,_SC("the object must be a weakref"));
+       }
+       v->Push(_weakref(o)->_obj);
+       return SQ_OK;
+}
+
+SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t)
+{
+       SQSharedState *ss = _ss(v);
+       switch(t) {
+       case OT_TABLE: v->Push(ss->_table_default_delegate); break;
+       case OT_ARRAY: v->Push(ss->_array_default_delegate); break;
+       case OT_STRING: v->Push(ss->_string_default_delegate); break;
+       case OT_INTEGER: case OT_FLOAT: v->Push(ss->_number_default_delegate); break;
+       case OT_GENERATOR: v->Push(ss->_generator_default_delegate); break;
+       case OT_CLOSURE: case OT_NATIVECLOSURE: v->Push(ss->_closure_default_delegate); break;
+       case OT_THREAD: v->Push(ss->_thread_default_delegate); break;
+       case OT_CLASS: v->Push(ss->_class_default_delegate); break;
+       case OT_INSTANCE: v->Push(ss->_instance_default_delegate); break;
+       case OT_WEAKREF: v->Push(ss->_weakref_default_delegate); break;
+       default: return sq_throwerror(v,_SC("the type doesn't have a default delegate"));
+       }
+       return SQ_OK;
+}
+
+SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx)
+{
+       SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val;
+       if(type(o) == OT_GENERATOR) {
+               return sq_throwerror(v,_SC("cannot iterate a generator"));
+       }
+       int faketojump;
+       if(!v->FOREACH_OP(o,realkey,val,refpos,0,666,faketojump))
+               return SQ_ERROR;
+       if(faketojump != 666) {
+               v->Push(realkey);
+               v->Push(val);
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+struct BufState{
+       const SQChar *buf;
+       SQInteger ptr;
+       SQInteger size;
+};
+
+SQInteger buf_lexfeed(SQUserPointer file)
+{
+       BufState *buf=(BufState*)file;
+       if(buf->size<(buf->ptr+1))
+               return 0;
+       return buf->buf[buf->ptr++];
+}
+
+SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) {
+       BufState buf;
+       buf.buf = s;
+       buf.size = size;
+       buf.ptr = 0;
+       return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror);
+}
+
+void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx)
+{
+       dest->Push(stack_get(src,idx));
+}
+
+void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc)
+{
+       _ss(v)->_printfunc = printfunc;
+}
+
+SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v)
+{
+       return _ss(v)->_printfunc;
+}
+
+void *sq_malloc(SQUnsignedInteger size)
+{
+       return SQ_MALLOC(size);
+}
+
+void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize)
+{
+       return SQ_REALLOC(p,oldsize,newsize);
+}
+
+void sq_free(void *p,SQUnsignedInteger size)
+{
+       SQ_FREE(p,size);
+}
diff --git a/src/squirrel/squirrel/sqarray.h b/src/squirrel/squirrel/sqarray.h
new file mode 100644 (file)
index 0000000..451b07d
--- /dev/null
@@ -0,0 +1,79 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQARRAY_H_
+#define _SQARRAY_H_
+
+struct SQArray : public CHAINABLE_OBJ
+{
+private:
+       SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+       ~SQArray()
+       {
+               REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+       }
+public:
+       static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){
+               SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray));
+               new (newarray) SQArray(ss,nInitialSize);
+               return newarray;
+       }
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+#endif
+       void Finalize(){
+               _values.resize(0);
+       }
+       bool Get(const SQInteger nidx,SQObjectPtr &val)
+       {
+               if(nidx>=0 && nidx<(SQInteger)_values.size()){
+                       SQObjectPtr &o = _values[nidx];
+                       val = _realval(o);
+                       return true;
+               }
+               else return false;
+       }
+       bool Set(const SQInteger nidx,const SQObjectPtr &val)
+       {
+               if(nidx>=0 && nidx<(SQInteger)_values.size()){
+                       _values[nidx]=val;
+                       return true;
+               }
+               else return false;
+       }
+       SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval)
+       {
+               SQUnsignedInteger idx=TranslateIndex(refpos);
+               while(idx<_values.size()){
+                       //first found
+                       outkey=(SQInteger)idx;
+                       SQObjectPtr &o = _values[idx];
+                       outval = _realval(o);
+                       //return idx for the next iteration
+                       return ++idx;
+               }
+               //nothing to iterate anymore
+               return -1;
+       }
+       SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),Size()); anew->_values.copy(_values); return anew; }
+       SQInteger Size() const {return _values.size();}
+       void Resize(SQInteger size,SQObjectPtr &fill = _null_) { _values.resize(size,fill); ShrinkIfNeeded(); }
+       void Reserve(SQInteger size) { _values.reserve(size); }
+       void Append(const SQObject &o){_values.push_back(o);}
+       void Extend(const SQArray *a);
+       SQObjectPtr &Top(){return _values.top();}
+       void Pop(){_values.pop_back(); ShrinkIfNeeded(); }
+       void Insert(const SQObject& idx,const SQObject &val){_values.insert((SQUnsignedInteger)tointeger(idx),val);}
+       void ShrinkIfNeeded() {
+               if(_values.size() <= _values.capacity()>>2) //shrink the array
+                       _values.shrinktofit();
+       }
+       void Remove(SQUnsignedInteger idx){
+               _values.remove(idx);
+               ShrinkIfNeeded();
+       }
+       void Release()
+       {
+               sq_delete(this,SQArray);
+       }
+       SQObjectPtrVec _values;
+};
+#endif //_SQARRAY_H_
diff --git a/src/squirrel/squirrel/sqbaselib.cpp b/src/squirrel/squirrel/sqbaselib.cpp
new file mode 100644 (file)
index 0000000..ee49b2d
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqclass.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+bool str2num(const SQChar *s,SQObjectPtr &res)
+{
+       SQChar *end;
+       if(scstrstr(s,_SC("."))){
+               SQFloat r = SQFloat(scstrtod(s,&end));
+               if(s == end) return false;
+               res = r;
+               return true;
+       }
+       else{
+               SQInteger r = SQInteger(scstrtol(s,&end,10));
+               if(s == end) return false;
+               res = r;
+               return true;
+       }
+}
+
+static SQInteger base_dummy(HSQUIRRELVM v)
+{
+       return 0;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+static SQInteger base_collectgarbage(HSQUIRRELVM v)
+{
+       sq_pushinteger(v, sq_collectgarbage(v));
+       return 1;
+}
+#endif
+
+static SQInteger base_getroottable(HSQUIRRELVM v)
+{
+       v->Push(v->_roottable);
+       return 1;
+}
+
+static SQInteger base_setroottable(HSQUIRRELVM v)
+{
+       SQObjectPtr &o=stack_get(v,2);
+       if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR;
+       v->Push(o);
+       return 1;
+}
+
+static SQInteger base_seterrorhandler(HSQUIRRELVM v)
+{
+       sq_seterrorhandler(v);
+       return 0;
+}
+
+static SQInteger base_setdebughook(HSQUIRRELVM v)
+{
+       sq_setdebughook(v);
+       return 0;
+}
+
+static SQInteger base_enabledebuginfo(HSQUIRRELVM v)
+{
+       SQObjectPtr &o=stack_get(v,2);
+       sq_enabledebuginfo(v,(type(o) != OT_NULL)?1:0);
+       return 0;
+}
+
+static SQInteger base_getstackinfos(HSQUIRRELVM v)
+{
+       SQInteger level;
+       SQStackInfos si;
+       SQInteger seq = 0;
+       const SQChar *name = NULL;
+       sq_getinteger(v, -1, &level);
+       if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si)))
+       {
+               const SQChar *fn = _SC("unknown");
+               const SQChar *src = _SC("unknown");
+               if(si.funcname)fn = si.funcname;
+               if(si.source)src = si.source;
+               sq_newtable(v);
+               sq_pushstring(v, _SC("func"), -1);
+               sq_pushstring(v, fn, -1);
+               sq_createslot(v, -3);
+               sq_pushstring(v, _SC("src"), -1);
+               sq_pushstring(v, src, -1);
+               sq_createslot(v, -3);
+               sq_pushstring(v, _SC("line"), -1);
+               sq_pushinteger(v, si.line);
+               sq_createslot(v, -3);
+               sq_pushstring(v, _SC("locals"), -1);
+               sq_newtable(v);
+               seq=0;
+               while ((name = sq_getlocal(v, level, seq))) {
+                       sq_pushstring(v, name, -1);
+                       sq_push(v, -2);
+                       sq_createslot(v, -4);
+                       sq_pop(v, 1);
+                       seq++;
+               }
+               sq_createslot(v, -3);
+               return 1;
+       }
+
+       return 0;
+}
+
+static SQInteger base_assert(HSQUIRRELVM v)
+{
+       if(v->IsFalse(stack_get(v,2))){
+               return sq_throwerror(v,_SC("assertion failed"));
+       }
+       return 0;
+}
+
+static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o)
+{
+       SQInteger top = sq_gettop(v);
+       sidx=0;
+       eidx=0;
+       o=stack_get(v,1);
+       SQObjectPtr &start=stack_get(v,2);
+       if(type(start)!=OT_NULL && sq_isnumeric(start)){
+               sidx=tointeger(start);
+       }
+       if(top>2){
+               SQObjectPtr &end=stack_get(v,3);
+               if(sq_isnumeric(end)){
+                       eidx=tointeger(end);
+               }
+       }
+       else {
+               eidx = sq_getsize(v,1);
+       }
+       return 1;
+}
+
+static SQInteger base_print(HSQUIRRELVM v)
+{
+       const SQChar *str;
+       sq_tostring(v,2);
+       sq_getstring(v,-1,&str);
+       if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,_SC("%s"),str);
+       return 0;
+}
+
+static SQInteger base_compilestring(HSQUIRRELVM v)
+{
+       SQInteger nargs=sq_gettop(v);
+       const SQChar *src=NULL,*name=_SC("unnamedbuffer");
+       SQInteger size;
+       sq_getstring(v,2,&src);
+       size=sq_getsize(v,2);
+       if(nargs>2){
+               sq_getstring(v,3,&name);
+       }
+       if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse)))
+               return 1;
+       else
+               return SQ_ERROR;
+}
+
+static SQInteger base_newthread(HSQUIRRELVM v)
+{
+       SQObjectPtr &func = stack_get(v,2);
+       SQInteger stksize = (_funcproto(_closure(func)->_function)->_stacksize << 1) +2;
+       HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize);
+       sq_move(newv,v,-2);
+       return 1;
+}
+
+static SQInteger base_suspend(HSQUIRRELVM v)
+{
+       return sq_suspendvm(v);
+}
+
+static SQInteger base_array(HSQUIRRELVM v)
+{
+       SQArray *a;
+       SQObject &size = stack_get(v,2);
+       if(sq_gettop(v) > 2) {
+               a = SQArray::Create(_ss(v),0);
+               a->Resize(tointeger(size),stack_get(v,3));
+       }
+       else {
+               a = SQArray::Create(_ss(v),tointeger(size));
+       }
+       v->Push(a);
+       return 1;
+}
+
+static SQInteger base_type(HSQUIRRELVM v)
+{
+       SQObjectPtr &o = stack_get(v,2);
+       v->Push(SQString::Create(_ss(v),GetTypeName(o),-1));
+       return 1;
+}
+
+static SQRegFunction base_funcs[]={
+       //generic
+       {_SC("seterrorhandler"),base_seterrorhandler,2, NULL},
+       {_SC("setdebughook"),base_setdebughook,2, NULL},
+       {_SC("enabledebuginfo"),base_enabledebuginfo,2, NULL},
+       {_SC("getstackinfos"),base_getstackinfos,2, _SC(".n")},
+       {_SC("getroottable"),base_getroottable,1, NULL},
+       {_SC("setroottable"),base_setroottable,2, NULL},
+       {_SC("assert"),base_assert,2, NULL},
+       {_SC("print"),base_print,2, NULL},
+       {_SC("compilestring"),base_compilestring,-2, _SC(".ss")},
+       {_SC("newthread"),base_newthread,2, _SC(".c")},
+       {_SC("suspend"),base_suspend,-1, NULL},
+       {_SC("array"),base_array,-2, _SC(".n")},
+       {_SC("type"),base_type,2, NULL},
+       {_SC("dummy"),base_dummy,0,NULL},
+#ifndef NO_GARBAGE_COLLECTOR
+       {_SC("collectgarbage"),base_collectgarbage,1, _SC("t")},
+#endif
+       {0,0}
+};
+
+void sq_base_register(HSQUIRRELVM v)
+{
+       SQInteger i=0;
+       sq_pushroottable(v);
+       while(base_funcs[i].name!=0) {
+               sq_pushstring(v,base_funcs[i].name,-1);
+               sq_newclosure(v,base_funcs[i].f,0);
+               sq_setnativeclosurename(v,-1,base_funcs[i].name);
+               sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask);
+               sq_createslot(v,-3);
+               i++;
+       }
+       sq_pushstring(v,_SC("_version_"),-1);
+       sq_pushstring(v,SQUIRREL_VERSION,-1);
+       sq_createslot(v,-3);
+       sq_pushstring(v,_SC("_charsize_"),-1);
+       sq_pushinteger(v,sizeof(SQChar));
+       sq_createslot(v,-3);
+       sq_pushstring(v,_SC("_intsize_"),-1);
+       sq_pushinteger(v,sizeof(SQInteger));
+       sq_createslot(v,-3);
+       sq_pop(v,1);
+}
+
+static SQInteger default_delegate_len(HSQUIRRELVM v)
+{
+       v->Push(SQInteger(sq_getsize(v,1)));
+       return 1;
+}
+
+static SQInteger default_delegate_tofloat(HSQUIRRELVM v)
+{
+       SQObjectPtr &o=stack_get(v,1);
+       switch(type(o)){
+       case OT_STRING:{
+               SQObjectPtr res;
+               if(str2num(_stringval(o),res)){
+                       v->Push(SQObjectPtr(tofloat(res)));
+                       break;
+               }}
+               return sq_throwerror(v, _SC("cannot convert the string"));
+               break;
+       case OT_INTEGER:case OT_FLOAT:
+               v->Push(SQObjectPtr(tofloat(o)));
+               break;
+       case OT_BOOL:
+               v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0)));
+               break;
+       default:
+               v->Push(_null_);
+               break;
+       }
+       return 1;
+}
+
+static SQInteger default_delegate_tointeger(HSQUIRRELVM v)
+{
+       SQObjectPtr &o=stack_get(v,1);
+       switch(type(o)){
+       case OT_STRING:{
+               SQObjectPtr res;
+               if(str2num(_stringval(o),res)){
+                       v->Push(SQObjectPtr(tointeger(res)));
+                       break;
+               }}
+               return sq_throwerror(v, _SC("cannot convert the string"));
+               break;
+       case OT_INTEGER:case OT_FLOAT:
+               v->Push(SQObjectPtr(tointeger(o)));
+               break;
+       case OT_BOOL:
+               v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0));
+               break;
+       default:
+               v->Push(_null_);
+               break;
+       }
+       return 1;
+}
+
+static SQInteger default_delegate_tostring(HSQUIRRELVM v)
+{
+       sq_tostring(v,1);
+       return 1;
+}
+
+static SQInteger obj_delegate_weakref(HSQUIRRELVM v)
+{
+       sq_weakref(v,1);
+       return 1;
+}
+
+static SQInteger obj_clear(HSQUIRRELVM v)
+{
+       return sq_clear(v,-1);
+}
+
+
+static SQInteger number_delegate_tochar(HSQUIRRELVM v)
+{
+       SQObject &o=stack_get(v,1);
+       SQChar c = (SQChar)tointeger(o);
+       v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1));
+       return 1;
+}
+
+
+/////////////////////////////////////////////////////////////////
+//TABLE DEFAULT DELEGATE
+
+static SQInteger table_rawdelete(HSQUIRRELVM v)
+{
+       if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue)))
+               return SQ_ERROR;
+       return 1;
+}
+
+
+static SQInteger container_rawexists(HSQUIRRELVM v)
+{
+       if(SQ_SUCCEEDED(sq_rawget(v,-2))) {
+               sq_pushbool(v,SQTrue);
+               return 1;
+       }
+       sq_pushbool(v,SQFalse);
+       return 1;
+}
+
+static SQInteger table_rawset(HSQUIRRELVM v)
+{
+       return sq_rawset(v,-3);
+}
+
+
+static SQInteger table_rawget(HSQUIRRELVM v)
+{
+       return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR;
+}
+
+
+SQRegFunction SQSharedState::_table_default_delegate_funcz[]={
+       {_SC("len"),default_delegate_len,1, _SC("t")},
+       {_SC("rawget"),table_rawget,2, _SC("t")},
+       {_SC("rawset"),table_rawset,3, _SC("t")},
+       {_SC("rawdelete"),table_rawdelete,2, _SC("t")},
+       {_SC("rawin"),container_rawexists,2, _SC("t")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("clear"),obj_clear,1, _SC(".")},
+       {0,0}
+};
+
+//ARRAY DEFAULT DELEGATE///////////////////////////////////////
+
+static SQInteger array_append(HSQUIRRELVM v)
+{
+       return sq_arrayappend(v,-2);
+}
+
+static SQInteger array_extend(HSQUIRRELVM v)
+{
+       _array(stack_get(v,1))->Extend(_array(stack_get(v,2)));
+       return 0;
+}
+
+static SQInteger array_reverse(HSQUIRRELVM v)
+{
+       return sq_arrayreverse(v,-1);
+}
+
+static SQInteger array_pop(HSQUIRRELVM v)
+{
+       return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR;
+}
+
+static SQInteger array_top(HSQUIRRELVM v)
+{
+       SQObject &o=stack_get(v,1);
+       if(_array(o)->Size()>0){
+               v->Push(_array(o)->Top());
+               return 1;
+       }
+       else return sq_throwerror(v,_SC("top() on a empty array"));
+}
+
+static SQInteger array_insert(HSQUIRRELVM v)
+{
+       SQObject &o=stack_get(v,1);
+       SQObject &idx=stack_get(v,2);
+       SQObject &val=stack_get(v,3);
+       _array(o)->Insert(idx,val);
+       return 0;
+}
+
+static SQInteger array_remove(HSQUIRRELVM v)
+{
+       SQObject &o = stack_get(v, 1);
+       SQObject &idx = stack_get(v, 2);
+       if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type"));
+       SQObjectPtr val;
+       if(_array(o)->Get(tointeger(idx), val)) {
+               _array(o)->Remove(tointeger(idx));
+               v->Push(val);
+               return 1;
+       }
+       return sq_throwerror(v, _SC("idx out of range"));
+}
+
+static SQInteger array_resize(HSQUIRRELVM v)
+{
+       SQObject &o = stack_get(v, 1);
+       SQObject &nsize = stack_get(v, 2);
+       SQObjectPtr fill;
+       if(sq_isnumeric(nsize)) {
+               if(sq_gettop(v) > 2)
+                       fill = stack_get(v, 3);
+               _array(o)->Resize(tointeger(nsize),fill);
+               return 0;
+       }
+       return sq_throwerror(v, _SC("size must be a number"));
+}
+
+
+//QSORT ala Sedgewick
+bool _qsort_compare(HSQUIRRELVM v,SQObjectPtr &arr,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret)
+{
+       if(func < 0) {
+               if(!v->ObjCmp(a,b,ret)) return false;
+       }
+       else {
+               SQInteger top = sq_gettop(v);
+               sq_push(v, func);
+               sq_pushroottable(v);
+               v->Push(a);
+               v->Push(b);
+               if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) {
+                       v->Raise_Error(_SC("compare func failed"));
+                       return false;
+               }
+               sq_getinteger(v, -1, &ret);
+               sq_settop(v, top);
+               return true;
+       }
+       return true;
+}
+//QSORT ala Sedgewick
+bool _qsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger l, SQInteger r,SQInteger func)
+{
+       SQInteger i, j;
+       SQArray *a=_array(arr);
+       SQObjectPtr pivot,t;
+       if( l < r ){
+               pivot = a->_values[l];
+               i = l; j = r+1;
+               while(1){
+                       SQInteger ret;
+                       do { 
+                               ++i; 
+                               if(i > r) break;
+                               if(!_qsort_compare(v,arr,a->_values[i],pivot,func,ret))
+                                       return false;
+                       } while( ret <= 0);
+                       do {
+                               --j; 
+                               if(!_qsort_compare(v,arr,a->_values[j],pivot,func,ret))
+                                       return false;
+                       }
+                       while( ret > 0 );
+                       if( i >= j ) break;
+                       t = a->_values[i]; a->_values[i] = a->_values[j]; a->_values[j] = t;
+               }
+               t = a->_values[l]; a->_values[l] = a->_values[j]; a->_values[j] = t;
+               if(!_qsort( v, arr, l, j-1,func)) return false;
+               if(!_qsort( v, arr, j+1, r,func)) return false;
+       }
+       return true;
+}
+
+static SQInteger array_sort(HSQUIRRELVM v)
+{
+       SQInteger func = -1;
+       SQObjectPtr &o = stack_get(v,1);
+       SQObject &funcobj = stack_get(v,2);
+       if(_array(o)->Size() > 1) {
+               if(type(funcobj) == OT_CLOSURE || type(funcobj) == OT_NATIVECLOSURE) func = 2;
+               if(!_qsort(v, o, 0, _array(o)->Size()-1, func))
+                       return SQ_ERROR;
+
+       }
+       return 0;
+}
+static SQInteger array_slice(HSQUIRRELVM v)
+{
+       SQInteger sidx,eidx;
+       SQObjectPtr o;
+       if(get_slice_params(v,sidx,eidx,o)==-1)return -1;
+       if(sidx<0)sidx=_array(o)->Size()+sidx;
+       if(eidx<0)eidx=_array(o)->Size()+eidx;
+       if(eidx < sidx)return sq_throwerror(v,_SC("wrong indexes"));
+       SQArray *arr=SQArray::Create(_ss(v),eidx-sidx);
+       SQObjectPtr t;
+       SQInteger count=0;
+       for(SQInteger i=sidx;i<eidx;i++){
+               _array(o)->Get(i,t);
+               arr->Set(count++,t);
+       }
+       v->Push(arr);
+       return 1;
+       
+}
+
+SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
+       {_SC("len"),default_delegate_len,1, _SC("a")},
+       {_SC("append"),array_append,2, _SC("a")},
+       {_SC("extend"),array_extend,2, _SC("aa")},
+       {_SC("push"),array_append,2, _SC("a")},
+       {_SC("pop"),array_pop,1, _SC("a")},
+       {_SC("top"),array_top,1, _SC("a")},
+       {_SC("insert"),array_insert,3, _SC("an")},
+       {_SC("remove"),array_remove,2, _SC("an")},
+       {_SC("resize"),array_resize,-2, _SC("an")},
+       {_SC("reverse"),array_reverse,1, _SC("a")},
+       {_SC("sort"),array_sort,-1, _SC("ac")},
+       {_SC("slice"),array_slice,-1, _SC("ann")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("clear"),obj_clear,1, _SC(".")},
+       {0,0}
+};
+
+//STRING DEFAULT DELEGATE//////////////////////////
+static SQInteger string_slice(HSQUIRRELVM v)
+{
+       SQInteger sidx,eidx;
+       SQObjectPtr o;
+       if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1;
+       if(sidx<0)sidx=_string(o)->_len+sidx;
+       if(eidx<0)eidx=_string(o)->_len+eidx;
+       if(eidx<sidx)
+               return sq_throwerror(v,_SC("wrong indexes"));
+       v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx));
+       return 1;
+}
+
+static SQInteger string_find(HSQUIRRELVM v)
+{
+       SQInteger top,start_idx=0;
+       const SQChar *str,*substr,*ret;
+       if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){
+               if(top>2)sq_getinteger(v,3,&start_idx);
+               if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){
+                       ret=scstrstr(&str[start_idx],substr);
+                       if(ret){
+                               sq_pushinteger(v,(SQInteger)(ret-str));
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+       return sq_throwerror(v,_SC("invalid param"));
+}
+
+#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \
+{ \
+       SQObject str=stack_get(v,1); \
+       SQInteger len=_string(str)->_len; \
+       const SQChar *sThis=_stringval(str); \
+       SQChar *sNew=(_ss(v)->GetScratchPad(rsl(len))); \
+       for(SQInteger i=0;i<len;i++) sNew[i]=func(sThis[i]); \
+       v->Push(SQString::Create(_ss(v),sNew,len)); \
+       return 1; \
+}
+
+
+STRING_TOFUNCZ(tolower)
+STRING_TOFUNCZ(toupper)
+
+SQRegFunction SQSharedState::_string_default_delegate_funcz[]={
+       {_SC("len"),default_delegate_len,1, _SC("s")},
+       {_SC("tointeger"),default_delegate_tointeger,1, _SC("s")},
+       {_SC("tofloat"),default_delegate_tofloat,1, _SC("s")},
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("slice"),string_slice,-1, _SC(" s n  n")},
+       {_SC("find"),string_find,-2, _SC("s s n ")},
+       {_SC("tolower"),string_tolower,1, _SC("s")},
+       {_SC("toupper"),string_toupper,1, _SC("s")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {0,0}
+};
+
+//INTEGER DEFAULT DELEGATE//////////////////////////
+SQRegFunction SQSharedState::_number_default_delegate_funcz[]={
+       {_SC("tointeger"),default_delegate_tointeger,1, _SC("n|b")},
+       {_SC("tofloat"),default_delegate_tofloat,1, _SC("n|b")},
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("tochar"),number_delegate_tochar,1, _SC("n|b")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {0,0}
+};
+
+//CLOSURE DEFAULT DELEGATE//////////////////////////
+static SQInteger closure_pcall(HSQUIRRELVM v)
+{
+       return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR;
+}
+
+static SQInteger closure_call(HSQUIRRELVM v)
+{
+       return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQTrue))?1:SQ_ERROR;
+}
+
+static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror)
+{
+       SQArray *aparams=_array(stack_get(v,2));
+       SQInteger nparams=aparams->Size();
+       v->Push(stack_get(v,1));
+       for(SQInteger i=0;i<nparams;i++)v->Push(aparams->_values[i]);
+       return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR;
+}
+
+static SQInteger closure_acall(HSQUIRRELVM v)
+{
+       return _closure_acall(v,SQTrue);
+}
+
+static SQInteger closure_pacall(HSQUIRRELVM v)
+{
+       return _closure_acall(v,SQFalse);
+}
+
+static SQInteger closure_bindenv(HSQUIRRELVM v)
+{
+       if(SQ_FAILED(sq_bindenv(v,1)))
+               return SQ_ERROR;
+       return 1;
+}
+
+static SQInteger closure_getinfos(HSQUIRRELVM v) {
+       SQObject o = stack_get(v,1);
+       SQTable *res = SQTable::Create(_ss(v),4);
+       if(type(o) == OT_CLOSURE) {
+               SQFunctionProto *f = _funcproto(_closure(o)->_function);
+               SQInteger nparams = f->_nparameters + (f->_varparams?1:0);
+               SQObjectPtr params = SQArray::Create(_ss(v),nparams);
+               for(SQInteger n = 0; n<f->_nparameters; n++) {
+                       _array(params)->Set((SQInteger)n,f->_parameters[n]);
+               }
+               if(f->_varparams) {
+                       _array(params)->Set(nparams-1,SQString::Create(_ss(v),_SC("..."),-1));
+               }
+               res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),false);
+               res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),f->_name);
+               res->NewSlot(SQString::Create(_ss(v),_SC("src"),-1),f->_sourcename);
+               res->NewSlot(SQString::Create(_ss(v),_SC("parameters"),-1),params);
+               res->NewSlot(SQString::Create(_ss(v),_SC("varargs"),-1),f->_varparams);
+       }
+       else { //OT_NATIVECLOSURE 
+               SQNativeClosure *nc = _nativeclosure(o);
+               res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),true);
+               res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),nc->_name);
+               res->NewSlot(SQString::Create(_ss(v),_SC("paramscheck"),-1),nc->_nparamscheck);
+               SQObjectPtr typecheck;
+               if(nc->_typecheck.size() > 0) {
+                       typecheck =
+                               SQArray::Create(_ss(v), nc->_typecheck.size());
+                       for(SQUnsignedInteger n = 0; n<nc->_typecheck.size(); n++) {
+                                       _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]);
+                       }
+               }
+               res->NewSlot(SQString::Create(_ss(v),_SC("typecheck"),-1),typecheck);
+       }
+       v->Push(res);
+       return 1;
+}
+
+
+SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={
+       {_SC("call"),closure_call,-1, _SC("c")},
+       {_SC("pcall"),closure_pcall,-1, _SC("c")},
+       {_SC("acall"),closure_acall,2, _SC("ca")},
+       {_SC("pacall"),closure_pacall,2, _SC("ca")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("bindenv"),closure_bindenv,2, _SC("c x|y|t")},
+       {_SC("getinfos"),closure_getinfos,1, _SC("c")},
+       {0,0}
+};
+
+//GENERATOR DEFAULT DELEGATE
+static SQInteger generator_getstatus(HSQUIRRELVM v)
+{
+       SQObject &o=stack_get(v,1);
+       switch(_generator(o)->_state){
+               case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),_SC("suspended")));break;
+               case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),_SC("running")));break;
+               case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),_SC("dead")));break;
+       }
+       return 1;
+}
+
+SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={
+       {_SC("getstatus"),generator_getstatus,1, _SC("g")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {0,0}
+};
+
+//THREAD DEFAULT DELEGATE
+
+static SQInteger thread_call(HSQUIRRELVM v)
+{
+       SQObjectPtr o = stack_get(v,1);
+       if(type(o) == OT_THREAD) {
+               SQInteger nparams = sq_gettop(v);
+               _thread(o)->Push(_thread(o)->_roottable);
+               for(SQInteger i = 2; i<(nparams+1); i++)
+                       sq_move(_thread(o),v,i);
+               if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQFalse))) {
+                       sq_move(v,_thread(o),-1);
+                       return 1;
+               }
+               return SQ_ERROR;
+       }
+       return sq_throwerror(v,_SC("wrong parameter"));
+}
+
+static SQInteger thread_wakeup(HSQUIRRELVM v)
+{
+       SQObjectPtr o = stack_get(v,1);
+       if(type(o) == OT_THREAD) {
+               SQVM *thread = _thread(o);
+               SQInteger state = sq_getvmstate(thread);
+               if(state != SQ_VMSTATE_SUSPENDED) {
+                       switch(state) {
+                               case SQ_VMSTATE_IDLE:
+                                       return sq_throwerror(v,_SC("cannot wakeup a idle thread"));
+                               break;
+                               case SQ_VMSTATE_RUNNING:
+                                       return sq_throwerror(v,_SC("cannot wakeup a running thread"));
+                               break;
+                       }
+               }
+                       
+               SQInteger wakeupret = sq_gettop(v)>1?1:0;
+               if(wakeupret) {
+                       sq_move(thread,v,2);
+               }
+               if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,1,SQFalse))) {
+                       sq_move(v,thread,-1);
+                       sq_pop(thread,1);
+                       if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) {
+                               sq_pop(thread,1);
+                       }
+                       return 1;
+               }
+               return SQ_ERROR;
+       }
+       return sq_throwerror(v,_SC("wrong parameter"));
+}
+
+static SQInteger thread_getstatus(HSQUIRRELVM v)
+{
+       SQObjectPtr &o = stack_get(v,1);
+       switch(sq_getvmstate(_thread(o))) {
+               case SQ_VMSTATE_IDLE:
+                       sq_pushstring(v,_SC("idle"),-1);
+               break;
+               case SQ_VMSTATE_RUNNING:
+                       sq_pushstring(v,_SC("running"),-1);
+               break;
+               case SQ_VMSTATE_SUSPENDED:
+                       sq_pushstring(v,_SC("suspended"),-1);
+               break;
+               default:
+                       return sq_throwerror(v,_SC("internal VM error"));
+       }
+       return 1;
+}
+
+SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = {
+       {_SC("call"), thread_call, -1, _SC("v")},
+       {_SC("wakeup"), thread_wakeup, -1, _SC("v")},
+       {_SC("getstatus"), thread_getstatus, 1, _SC("v")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {0,0},
+};
+
+static SQInteger class_getattributes(HSQUIRRELVM v)
+{
+       if(SQ_SUCCEEDED(sq_getattributes(v,-2)))
+               return 1;
+       return SQ_ERROR;
+}
+
+static SQInteger class_setattributes(HSQUIRRELVM v)
+{
+       if(SQ_SUCCEEDED(sq_setattributes(v,-3)))
+               return 1;
+       return SQ_ERROR;
+}
+
+static SQInteger class_instance(HSQUIRRELVM v)
+{
+       if(SQ_SUCCEEDED(sq_createinstance(v,-1)))
+               return 1;
+       return SQ_ERROR;
+}
+
+SQRegFunction SQSharedState::_class_default_delegate_funcz[] = {
+       {_SC("getattributes"), class_getattributes, 2, _SC("y.")},
+       {_SC("setattributes"), class_setattributes, 3, _SC("y..")},
+       {_SC("rawin"),container_rawexists,2, _SC("y")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {_SC("instance"),class_instance,1, _SC("y")},
+       {0,0}
+};
+
+static SQInteger instance_getclass(HSQUIRRELVM v)
+{
+       if(SQ_SUCCEEDED(sq_getclass(v,1)))
+               return 1;
+       return SQ_ERROR;
+}
+
+SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = {
+       {_SC("getclass"), instance_getclass, 1, _SC("x")},
+       {_SC("rawin"),container_rawexists,2, _SC("x")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {0,0}
+};
+
+static SQInteger weakref_ref(HSQUIRRELVM v)
+{
+       if(SQ_FAILED(sq_getweakrefval(v,1)))
+               return SQ_ERROR;
+       return 1;
+}
+
+SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = {
+       {_SC("ref"),weakref_ref,1, _SC("r")},
+       {_SC("weakref"),obj_delegate_weakref,1, NULL },
+       {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+       {0,0}
+};
+
+
diff --git a/src/squirrel/squirrel/sqclass.cpp b/src/squirrel/squirrel/sqclass.cpp
new file mode 100644 (file)
index 0000000..309036a
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqtable.h"
+#include "sqclass.h"
+#include "sqclosure.h"
+
+SQClass::SQClass(SQSharedState *ss,SQClass *base)
+{
+       _base = base;
+       _typetag = 0;
+       _hook = NULL;
+       _udsize = 0;
+       _metamethods.resize(MT_LAST); //size it to max size
+       if(_base) {
+               _defaultvalues.copy(base->_defaultvalues);
+               _methods.copy(base->_methods);
+               _metamethods.copy(base->_metamethods);
+               __ObjAddRef(_base);
+       }
+       _members = base?base->_members->Clone() : SQTable::Create(ss,0);
+       __ObjAddRef(_members);
+       _locked = false;
+       INIT_CHAIN();
+       ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
+}
+
+void SQClass::Finalize() { 
+       _attributes = _null_;
+       _defaultvalues.resize(0);
+       _methods.resize(0);
+       _metamethods.resize(0);
+       __ObjRelease(_members);
+       if(_base) {
+               __ObjRelease(_base);
+       }
+}
+
+SQClass::~SQClass()
+{
+       REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+       Finalize();
+}
+
+bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
+{
+       SQObjectPtr temp;
+       if(_locked) 
+               return false; //the class already has an instance so cannot be modified
+       if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value
+       {
+               _defaultvalues[_member_idx(temp)].val = val;
+               return true;
+       }
+       if(type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE || bstatic) {
+               SQInteger mmidx;
+               if((type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE) && 
+                       (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) {
+                       _metamethods[mmidx] = val;
+               } 
+               else {
+                       if(type(temp) == OT_NULL) {
+                               SQClassMember m;
+                               m.val = val;
+                               _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size())));
+                               _methods.push_back(m);
+                       }
+                       else {
+                               _methods[_member_idx(temp)].val = val;
+                       }
+               }
+               return true;
+       }
+       SQClassMember m;
+       m.val = val;
+       _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size())));
+       _defaultvalues.push_back(m);
+       return true;
+}
+
+SQInstance *SQClass::CreateInstance()
+{
+       if(!_locked) Lock();
+       return SQInstance::Create(_opt_ss(this),this);
+}
+
+SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+       SQObjectPtr oval;
+       SQInteger idx = _members->Next(false,refpos,outkey,oval);
+       if(idx != -1) {
+               if(_ismethod(oval)) {
+                       outval = _methods[_member_idx(oval)].val;
+               }
+               else {
+                       SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val;
+                       outval = _realval(o);
+               }
+       }
+       return idx;
+}
+
+bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val)
+{
+       SQObjectPtr idx;
+       if(_members->Get(key,idx)) {
+               if(_isfield(idx))
+                       _defaultvalues[_member_idx(idx)].attrs = val;
+               else
+                       _methods[_member_idx(idx)].attrs = val;
+               return true;
+       }
+       return false;
+}
+
+bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval)
+{
+       SQObjectPtr idx;
+       if(_members->Get(key,idx)) {
+               outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs);
+               return true;
+       }
+       return false;
+}
+
+///////////////////////////////////////////////////////////////////////
+void SQInstance::Init(SQSharedState *ss)
+{
+       _userpointer = NULL;
+       _hook = NULL;
+       __ObjAddRef(_class);
+       _delegate = _class->_members;
+       INIT_CHAIN();
+       ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
+}
+
+SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize)
+{
+       _memsize = memsize;
+       _class = c;
+       SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+       for(SQUnsignedInteger n = 0; n < nvalues; n++) {
+               new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val);
+       }
+       Init(ss);
+}
+
+SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize)
+{
+       _memsize = memsize;
+       _class = i->_class;
+       SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+       for(SQUnsignedInteger n = 0; n < nvalues; n++) {
+               new (&_values[n]) SQObjectPtr(i->_values[n]);
+       }
+       Init(ss);
+}
+
+void SQInstance::Finalize() 
+{
+       SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+       __ObjRelease(_class);
+       for(SQUnsignedInteger i = 0; i < nvalues; i++) {
+               _values[i] = _null_;
+       }
+}
+
+SQInstance::~SQInstance()
+{
+       REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+       if(_class){ Finalize(); } //if _class is null it was already finalized by the GC
+}
+
+bool SQInstance::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res)
+{
+       if(type(_class->_metamethods[mm]) != OT_NULL) {
+               res = _class->_metamethods[mm];
+               return true;
+       }
+       return false;
+}
+
+bool SQInstance::InstanceOf(SQClass *trg)
+{
+       SQClass *parent = _class;
+       while(parent != NULL) {
+               if(parent == trg)
+                       return true;
+               parent = parent->_base;
+       }
+       return false;
+}
diff --git a/src/squirrel/squirrel/sqclass.h b/src/squirrel/squirrel/sqclass.h
new file mode 100644 (file)
index 0000000..4faff83
--- /dev/null
@@ -0,0 +1,152 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQCLASS_H_
+#define _SQCLASS_H_
+
+struct SQInstance;
+
+struct SQClassMember {
+       SQClassMember(){}
+       SQClassMember(const SQClassMember &o) {
+               val = o.val;
+               attrs = o.attrs;
+       }
+       SQObjectPtr val;
+       SQObjectPtr attrs;
+};
+
+typedef sqvector<SQClassMember> SQClassMemberVec;
+
+#define MEMBER_TYPE_METHOD 0x01000000
+#define MEMBER_TYPE_FIELD 0x02000000
+
+#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD)
+#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD)
+#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i))
+#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i))
+#define _member_type(o) (_integer(o)&0xFF000000)
+#define _member_idx(o) (_integer(o)&0x00FFFFFF)
+
+struct SQClass : public CHAINABLE_OBJ
+{
+       SQClass(SQSharedState *ss,SQClass *base);
+public:
+       static SQClass* Create(SQSharedState *ss,SQClass *base) {
+               SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass));
+               new (newclass) SQClass(ss, base);
+               return newclass;
+       }
+       ~SQClass();
+       bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic);
+       bool Get(const SQObjectPtr &key,SQObjectPtr &val) {
+               if(_members->Get(key,val)) {
+                       if(_isfield(val)) {
+                               SQObjectPtr &o = _defaultvalues[_member_idx(val)].val;
+                               val = _realval(o);
+                       }
+                       else {
+                               val = _methods[_member_idx(val)].val;
+                       }
+                       return true;
+               }
+               return false;
+       }
+       bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val);
+       bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval);
+       void Lock() { _locked = true; if(_base) _base->Lock(); }
+       void Release() { 
+               if (_hook) { _hook(_typetag,0);}
+               sq_delete(this, SQClass);       
+       }
+       void Finalize();
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable ** );
+#endif
+       SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+       SQInstance *CreateInstance();
+       SQTable *_members;
+       SQClass *_base;
+       SQClassMemberVec _defaultvalues;
+       SQClassMemberVec _methods;
+       SQObjectPtrVec _metamethods;
+       SQObjectPtr _attributes;
+       SQUserPointer _typetag;
+       SQRELEASEHOOK _hook;
+       bool _locked;
+       SQInteger _udsize;
+};
+
+#define calcinstancesize(_theclass_) \
+       (_theclass_->_udsize + sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))
+
+struct SQInstance : public SQDelegable 
+{
+       void Init(SQSharedState *ss);
+       SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize);
+       SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize);
+public:
+       static SQInstance* Create(SQSharedState *ss,SQClass *theclass) {
+               
+               SQInteger size = calcinstancesize(theclass);
+               SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
+               new (newinst) SQInstance(ss, theclass,size);
+               if(theclass->_udsize) {
+                       newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize);
+               }
+               return newinst;
+       }
+       SQInstance *Clone(SQSharedState *ss)
+       {
+               SQInteger size = calcinstancesize(_class);
+               SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
+               new (newinst) SQInstance(ss, this,size);
+               if(_class->_udsize) {
+                       newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize);
+               }
+               return newinst;
+       }
+       ~SQInstance();
+       bool Get(const SQObjectPtr &key,SQObjectPtr &val)  {
+               if(_class->_members->Get(key,val)) {
+                       if(_isfield(val)) {
+                               SQObjectPtr &o = _values[_member_idx(val)];
+                               val = _realval(o);
+                       }
+                       else {
+                               val = _class->_methods[_member_idx(val)].val;
+                       }
+                       return true;
+               }
+               return false;
+       }
+       bool Set(const SQObjectPtr &key,const SQObjectPtr &val) {
+               SQObjectPtr idx;
+               if(_class->_members->Get(key,idx) && _isfield(idx)) {
+            _values[_member_idx(idx)] = val;
+                       return true;
+               }
+               return false;
+       }
+       void Release() {
+               _uiRef++;
+               if (_hook) { _hook(_userpointer,0);}
+               _uiRef--;
+               if(_uiRef > 0) return;
+               SQInteger size = _memsize;
+               this->~SQInstance();
+               SQ_FREE(this, size);
+       }
+       void Finalize();
+#ifndef NO_GARBAGE_COLLECTOR 
+       void Mark(SQCollectable ** );
+#endif
+       bool InstanceOf(SQClass *trg);
+       bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
+
+       SQClass *_class;
+       SQUserPointer _userpointer;
+       SQRELEASEHOOK _hook;
+       SQInteger _memsize;
+       SQObjectPtr _values[1];
+};
+
+#endif //_SQCLASS_H_
diff --git a/src/squirrel/squirrel/sqclosure.h b/src/squirrel/squirrel/sqclosure.h
new file mode 100644 (file)
index 0000000..1c9eaa9
--- /dev/null
@@ -0,0 +1,120 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQCLOSURE_H_
+#define _SQCLOSURE_H_
+
+struct SQFunctionProto;
+
+struct SQClosure : public CHAINABLE_OBJ
+{
+private:
+       SQClosure(SQSharedState *ss,SQFunctionProto *func){_function=func; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+public:
+       static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){
+               SQClosure *nc=(SQClosure*)SQ_MALLOC(sizeof(SQClosure));
+               new (nc) SQClosure(ss,func);
+               return nc;
+       }
+       void Release(){
+               sq_delete(this,SQClosure);
+       }
+       SQClosure *Clone()
+       {
+               SQClosure * ret = SQClosure::Create(_opt_ss(this),_funcproto(_function));
+               ret->_env = _env;
+               ret->_outervalues.copy(_outervalues);
+               return ret;
+       }
+       ~SQClosure()
+       {
+               REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+       }
+       bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
+       static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+       void Finalize(){_outervalues.resize(0); }
+#endif
+       SQObjectPtr _env;
+       SQObjectPtr _function;
+       SQObjectPtrVec _outervalues;
+};
+//////////////////////////////////////////////
+struct SQGenerator : public CHAINABLE_OBJ 
+{
+       enum SQGeneratorState{eRunning,eSuspended,eDead};
+private:
+       SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=_null_;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+public:
+       static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){
+               SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator));
+               new (nc) SQGenerator(ss,closure);
+               return nc;
+       }
+       ~SQGenerator()
+       {
+               REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+       }
+    void Kill(){
+               _state=eDead;
+               _stack.resize(0);
+               _closure=_null_;}
+       void Release(){
+               sq_delete(this,SQGenerator);
+       }
+       bool Yield(SQVM *v);
+       bool Resume(SQVM *v,SQInteger target);
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+       void Finalize(){_stack.resize(0);_closure=_null_;}
+#endif
+       SQObjectPtr _closure;
+       SQObjectPtrVec _stack;
+       SQObjectPtrVec _vargsstack;
+       SQVM::CallInfo _ci;
+       ExceptionsTraps _etraps;
+       SQGeneratorState _state;
+};
+
+struct SQNativeClosure : public CHAINABLE_OBJ
+{
+private:
+       SQNativeClosure(SQSharedState *ss,SQFUNCTION func){_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);        }
+public:
+       static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func)
+       {
+               SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(sizeof(SQNativeClosure));
+               new (nc) SQNativeClosure(ss,func);
+               return nc;
+       }
+       SQNativeClosure *Clone()
+       {
+               SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function);
+               ret->_env = _env;
+               ret->_name = _name;
+               ret->_outervalues.copy(_outervalues);
+               ret->_typecheck.copy(_typecheck);
+               ret->_nparamscheck = _nparamscheck;
+               return ret;
+       }
+       ~SQNativeClosure()
+       {
+               REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+       }
+       void Release(){
+               sq_delete(this,SQNativeClosure);
+       }
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+       void Finalize(){_outervalues.resize(0);}
+#endif
+       SQInteger _nparamscheck;
+       SQIntVec _typecheck;
+       SQObjectPtrVec _outervalues;
+       SQObjectPtr _env;
+       SQFUNCTION _function;
+       SQObjectPtr _name;
+};
+
+
+
+#endif //_SQCLOSURE_H_
diff --git a/src/squirrel/squirrel/sqcompiler.cpp b/src/squirrel/squirrel/sqcompiler.cpp
new file mode 100644 (file)
index 0000000..7137c53
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <stdarg.h>
+#include <setjmp.h>
+#include "sqopcodes.h"
+#include "sqstring.h"
+#include "sqfuncproto.h"
+#include "sqcompiler.h"
+#include "sqfuncstate.h"
+#include "sqlexer.h"
+#include "sqvm.h"
+
+#define DEREF_NO_DEREF -1
+#define DEREF_FIELD            -2
+
+struct ExpState
+{
+       ExpState()
+       {
+               _deref = DEREF_NO_DEREF;
+               _freevar = false;
+               _class_or_delete = false;
+               _funcarg = false;
+       }
+       bool _class_or_delete;
+       bool _funcarg;
+       bool _freevar;
+       SQInteger _deref;
+};
+
+typedef sqvector<ExpState> ExpStateVec;
+
+#define _exst (_expstates.top())
+
+#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \
+                                                       SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \
+                                                       _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);
+
+#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \
+                                       __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \
+                                       if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \
+                                       if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \
+                                       _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}
+
+class SQCompiler
+{
+public:
+       SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo)
+       {
+               _vm=v;
+               _lex.Init(_ss(v), rg, up,ThrowError,this);
+               _sourcename = SQString::Create(_ss(v), sourcename);
+               _lineinfo = lineinfo;_raiseerror = raiseerror;
+               compilererror = NULL;
+       }
+       static void ThrowError(void *ud, const SQChar *s) {
+               SQCompiler *c = (SQCompiler *)ud;
+               c->Error(s);
+       }
+       void Error(const SQChar *s, ...)
+       {
+               static SQChar temp[256];
+               va_list vl;
+               va_start(vl, s);
+               scvsprintf(temp, s, vl);
+               va_end(vl);
+               compilererror = temp;
+               longjmp(_errorjmp,1);
+       }
+       void Lex(){     _token = _lex.Lex();}
+       void PushExpState(){ _expstates.push_back(ExpState()); }
+       bool IsDerefToken(SQInteger tok)
+       {
+               switch(tok){
+               case _SC('='): case _SC('('): case TK_NEWSLOT:
+               case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true;
+               }
+               return false;
+       }
+       ExpState PopExpState()
+       {
+               ExpState ret = _expstates.top();
+               _expstates.pop_back();
+               return ret;
+       }
+       SQObject Expect(SQInteger tok)
+       {
+               
+               if(_token != tok) {
+                       if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) {
+                               //ret = SQString::Create(_ss(_vm),_SC("constructor"));
+                               //do nothing
+                       }
+                       else {
+                               const SQChar *etypename;
+                               if(tok > 255) {
+                                       switch(tok)
+                                       {
+                                       case TK_IDENTIFIER:
+                                               etypename = _SC("IDENTIFIER");
+                                               break;
+                                       case TK_STRING_LITERAL:
+                                               etypename = _SC("STRING_LITERAL");
+                                               break;
+                                       case TK_INTEGER:
+                                               etypename = _SC("INTEGER");
+                                               break;
+                                       case TK_FLOAT:
+                                               etypename = _SC("FLOAT");
+                                               break;
+                                       default:
+                                               etypename = _lex.Tok2Str(tok);
+                                       }
+                                       Error(_SC("expected '%s'"), etypename);
+                               }
+                               Error(_SC("expected '%c'"), tok);
+                       }
+               }
+               SQObjectPtr ret;
+               switch(tok)
+               {
+               case TK_IDENTIFIER:
+                       ret = _fs->CreateString(_lex._svalue);
+                       break;
+               case TK_STRING_LITERAL:
+                       ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);
+                       break;
+               case TK_INTEGER:
+                       ret = SQObjectPtr(_lex._nvalue);
+                       break;
+               case TK_FLOAT:
+                       ret = SQObjectPtr(_lex._fvalue);
+                       break;
+               }
+               Lex();
+               return ret;
+       }
+       bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); }
+       void OptionalSemicolon()
+       {
+               if(_token == _SC(';')) { Lex(); return; }
+               if(!IsEndOfStatement()) {
+                       Error(_SC("end of statement expected (; or lf)"));
+               }
+       }
+       void MoveIfCurrentTargetIsLocal() {
+               SQInteger trg = _fs->TopTarget();
+               if(_fs->IsLocal(trg)) {
+                       trg = _fs->PopTarget(); //no pops the target and move it
+                       _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);
+               }
+       }
+       bool Compile(SQObjectPtr &o)
+       {
+               _debugline = 1;
+               _debugop = 0;
+
+               SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this);
+               funcstate._name = SQString::Create(_ss(_vm), _SC("main"));
+               _fs = &funcstate;
+               _fs->AddParameter(_fs->CreateString(_SC("this")));
+               _fs->_sourcename = _sourcename;
+               SQInteger stacksize = _fs->GetStackSize();
+               if(setjmp(_errorjmp) == 0) {
+                       Lex();
+                       while(_token > 0){
+                               Statement();
+                               if(_lex._prevtoken != _SC('}')) OptionalSemicolon();
+                       }
+                       CleanStack(stacksize);
+                       _fs->AddLineInfos(_lex._currentline, _lineinfo, true);
+                       _fs->AddInstruction(_OP_RETURN, 0xFF);
+                       _fs->SetStackSize(0);
+                       o =_fs->BuildProto();
+#ifdef _DEBUG_DUMP
+                       _fs->Dump(_funcproto(o));
+#endif
+               }
+               else {
+                       if(_raiseerror && _ss(_vm)->_compilererrorhandler) {
+                               _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"),
+                                       _lex._currentline, _lex._currentcolumn);
+                       }
+                       _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1);
+                       return false;
+               }
+               return true;
+       }
+       void Statements()
+       {
+               while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {
+                       Statement();
+                       if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();
+               }
+       }
+       void Statement()
+       {
+               _fs->AddLineInfos(_lex._currentline, _lineinfo);
+               switch(_token){
+               case _SC(';'):  Lex();                                  break;
+               case TK_IF:             IfStatement();                  break;
+               case TK_WHILE:          WhileStatement();               break;
+               case TK_DO:             DoWhileStatement();             break;
+               case TK_FOR:            ForStatement();                 break;
+               case TK_FOREACH:        ForEachStatement();             break;
+               case TK_SWITCH: SwitchStatement();              break;
+               case TK_LOCAL:          LocalDeclStatement();   break;
+               case TK_RETURN:
+               case TK_YIELD: {
+                       SQOpcode op;
+                       if(_token == TK_RETURN) {
+                               op = _OP_RETURN;
+                               
+                       }
+                       else {
+                               op = _OP_YIELD;
+                               _fs->_bgenerator = true;
+                       }
+                       Lex();
+                       if(!IsEndOfStatement()) {
+                               SQInteger retexp = _fs->GetCurrentPos()+1;
+                               CommaExpr();
+                               if(op == _OP_RETURN && _fs->_traps > 0)
+                                       _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0);
+                               _fs->_returnexp = retexp;
+                               _fs->AddInstruction(op, 1, _fs->PopTarget());
+                       }
+                       else{ 
+                               if(op == _OP_RETURN && _fs->_traps > 0)
+                                       _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0);
+                               _fs->_returnexp = -1;
+                               _fs->AddInstruction(op, 0xFF); 
+                       }
+                       break;}
+               case TK_BREAK:
+                       if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block"));
+                       if(_fs->_breaktargets.top() > 0){
+                               _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
+                       }
+                       _fs->AddInstruction(_OP_JMP, 0, -1234);
+                       _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos());
+                       Lex();
+                       break;
+               case TK_CONTINUE:
+                       if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block"));
+                       if(_fs->_continuetargets.top() > 0) {
+                               _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
+                       }
+                       _fs->AddInstruction(_OP_JMP, 0, -1234);
+                       _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos());
+                       Lex();
+                       break;
+               case TK_FUNCTION:
+                       FunctionStatement();
+                       break;
+               case TK_CLASS:
+                       ClassStatement();
+                       break;
+               case _SC('{'):{
+                               SQInteger stacksize = _fs->GetStackSize();
+                               Lex();
+                               Statements();
+                               Expect(_SC('}'));
+                               _fs->SetStackSize(stacksize);
+                       }
+                       break;
+               case TK_TRY:
+                       TryCatchStatement();
+                       break;
+               case TK_THROW:
+                       Lex();
+                       CommaExpr();
+                       _fs->AddInstruction(_OP_THROW, _fs->PopTarget());
+                       break;
+               default:
+                       CommaExpr();
+                       _fs->PopTarget();
+                       break;
+               }
+               _fs->SnoozeOpt();
+       }
+       void EmitDerefOp(SQOpcode op)
+       {
+               SQInteger val = _fs->PopTarget();
+               SQInteger key = _fs->PopTarget();
+               SQInteger src = _fs->PopTarget();
+        _fs->AddInstruction(op,_fs->PushTarget(),src,key,val);
+       }
+       void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0)
+       {
+               SQInteger p2 = _fs->PopTarget(); //src in OP_GET
+               SQInteger p1 = _fs->PopTarget(); //key in OP_GET
+               _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3);
+       }
+       void EmitCompoundArith(SQInteger tok,bool deref)
+       {
+               SQInteger oper;
+               switch(tok){
+               case TK_MINUSEQ: oper = '-'; break;
+               case TK_PLUSEQ: oper = '+'; break;
+               case TK_MULEQ: oper = '*'; break;
+               case TK_DIVEQ: oper = '/'; break;
+               case TK_MODEQ: oper = '%'; break;
+               default: oper = 0; //shut up compiler
+                       assert(0); break;
+               };
+               if(deref) {
+                       SQInteger val = _fs->PopTarget();
+                       SQInteger key = _fs->PopTarget();
+                       SQInteger src = _fs->PopTarget();
+                       //mixes dest obj and source val in the arg1(hack?)
+                       _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper);
+               }
+               else {
+                       Emit2ArgsOP(_OP_COMPARITHL, oper);
+               }
+       }
+       void CommaExpr()
+       {
+               for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr());
+       }
+       ExpState Expression(bool funcarg = false)
+       {
+               PushExpState();
+               _exst._class_or_delete = false;
+               _exst._funcarg = funcarg;
+               LogicalOrExp();
+               switch(_token)  {
+               case _SC('='):
+               case TK_NEWSLOT:
+               case TK_MINUSEQ:
+               case TK_PLUSEQ:
+               case TK_MULEQ:
+               case TK_DIVEQ:
+               case TK_MODEQ:
+               {
+                               SQInteger op = _token;
+                               SQInteger ds = _exst._deref;
+                               bool freevar = _exst._freevar;
+                               if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression"));
+                               Lex(); Expression();
+
+                               switch(op){
+                               case TK_NEWSLOT:
+                                       if(freevar) Error(_SC("free variables cannot be modified"));
+                                       if(ds == DEREF_FIELD)
+                                               EmitDerefOp(_OP_NEWSLOT);
+                                       else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+                                               Error(_SC("can't 'create' a local slot"));
+                                       break;
+                               case _SC('='): //ASSIGN
+                                       if(freevar) Error(_SC("free variables cannot be modified"));
+                                       if(ds == DEREF_FIELD)
+                                               EmitDerefOp(_OP_SET);
+                                       else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+                                               SQInteger p2 = _fs->PopTarget(); //src in OP_GET
+                                               SQInteger p1 = _fs->TopTarget(); //key in OP_GET
+                                               _fs->AddInstruction(_OP_MOVE, p1, p2);
+                                       }
+                                       break;
+                               case TK_MINUSEQ:
+                               case TK_PLUSEQ:
+                               case TK_MULEQ:
+                               case TK_DIVEQ:
+                               case TK_MODEQ:
+                                       EmitCompoundArith(op,ds == DEREF_FIELD);
+                                       break;
+                               }
+                       }
+                       break;
+               case _SC('?'): {
+                       Lex();
+                       _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+                       SQInteger jzpos = _fs->GetCurrentPos();
+                       SQInteger trg = _fs->PushTarget();
+                       Expression();
+                       SQInteger first_exp = _fs->PopTarget();
+                       if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+                       SQInteger endfirstexp = _fs->GetCurrentPos();
+                       _fs->AddInstruction(_OP_JMP, 0, 0);
+                       Expect(_SC(':'));
+                       SQInteger jmppos = _fs->GetCurrentPos();
+                       Expression();
+                       SQInteger second_exp = _fs->PopTarget();
+                       if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+                       _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
+                       _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1);
+                       _fs->SnoozeOpt();
+                       }
+                       break;
+               }
+               return PopExpState();
+       }
+       void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),SQInteger op3 = 0)
+       {
+               Lex(); (this->*f)();
+               SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget();
+               _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3);
+       }
+       void LogicalOrExp()
+       {
+               LogicalAndExp();
+               for(;;) if(_token == TK_OR) {
+                       SQInteger first_exp = _fs->PopTarget();
+                       SQInteger trg = _fs->PushTarget();
+                       _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0);
+                       SQInteger jpos = _fs->GetCurrentPos();
+                       if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+                       Lex(); LogicalOrExp();
+                       _fs->SnoozeOpt();
+                       SQInteger second_exp = _fs->PopTarget();
+                       if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+                       _fs->SnoozeOpt();
+                       _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
+                       break;
+               }else return;
+       }
+       void LogicalAndExp()
+       {
+               BitwiseOrExp();
+               for(;;) switch(_token) {
+               case TK_AND: {
+                       SQInteger first_exp = _fs->PopTarget();
+                       SQInteger trg = _fs->PushTarget();
+                       _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0);
+                       SQInteger jpos = _fs->GetCurrentPos();
+                       if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+                       Lex(); LogicalAndExp();
+                       _fs->SnoozeOpt();
+                       SQInteger second_exp = _fs->PopTarget();
+                       if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+                       _fs->SnoozeOpt();
+                       _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
+                       break;
+                       }
+               case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break;
+               case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break;
+               default:
+                       return;
+               }
+       }
+       void BitwiseOrExp()
+       {
+               BitwiseXorExp();
+               for(;;) if(_token == _SC('|'))
+               {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR);
+               }else return;
+       }
+       void BitwiseXorExp()
+       {
+               BitwiseAndExp();
+               for(;;) if(_token == _SC('^'))
+               {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR);
+               }else return;
+       }
+       void BitwiseAndExp()
+       {
+               CompExp();
+               for(;;) if(_token == _SC('&'))
+               {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND);
+               }else return;
+       }
+       void CompExp()
+       {
+               ShiftExp();
+               for(;;) switch(_token) {
+               case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break;
+               case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break;
+               case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break;
+               case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break;
+               case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break;
+               case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break;
+               default: return;        
+               }
+       }
+       void ShiftExp()
+       {
+               PlusExp();
+               for(;;) switch(_token) {
+               case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break;
+               case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break;
+               case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break;
+               default: return;        
+               }
+       }
+       void PlusExp()
+       {
+               MultExp();
+               for(;;) switch(_token) {
+               case _SC('+'): case _SC('-'):
+                       BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break;
+               default: return;
+               }
+       }
+       
+       void MultExp()
+       {
+               PrefixedExpr();
+               for(;;) switch(_token) {
+               case _SC('*'): case _SC('/'): case _SC('%'):
+                       BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break;
+               default: return;
+               }
+       }
+       //if 'pos' != -1 the previous variable is a local variable
+       void PrefixedExpr()
+       {
+               SQInteger pos = Factor();
+               for(;;) {
+                       switch(_token) {
+                       case _SC('.'): {
+                               pos = -1;
+                               Lex(); 
+                               if(_token == TK_PARENT) {
+                                       Lex();
+                                       if(!NeedGet())
+                                               Error(_SC("parent cannot be set"));
+                                       SQInteger src = _fs->PopTarget();
+                                       _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
+                               }
+                               else {
+                                       _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
+                                       if(NeedGet()) Emit2ArgsOP(_OP_GET);
+                               }
+                               _exst._deref = DEREF_FIELD;
+                               _exst._freevar = false;
+                               }
+                               break;
+                       case _SC('['):
+                               if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration"));
+                               Lex(); Expression(); Expect(_SC(']')); 
+                               pos = -1;
+                               if(NeedGet()) Emit2ArgsOP(_OP_GET);
+                               _exst._deref = DEREF_FIELD;
+                               _exst._freevar = false;
+                               break;
+                       case TK_MINUSMINUS:
+                       case TK_PLUSPLUS:
+                       if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) { 
+                               SQInteger tok = _token; Lex();
+                               if(pos < 0)
+                                       Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1);
+                               else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+                                       SQInteger src = _fs->PopTarget();
+                                       _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1);
+                               }
+                               
+                       }
+                       return;
+                       break;  
+                       case _SC('('): 
+                               {
+                               if(_exst._deref != DEREF_NO_DEREF) {
+                                       if(pos<0) {
+                                               SQInteger key = _fs->PopTarget(); //key
+                                               SQInteger table = _fs->PopTarget(); //table etc...
+                                               SQInteger closure = _fs->PushTarget();
+                                               SQInteger ttarget = _fs->PushTarget();
+                                               _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget);
+                                       }
+                                       else{
+                                               _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
+                                       }
+                               }
+                               else
+                                       _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
+                               _exst._deref = DEREF_NO_DEREF;
+                               Lex();
+                               FunctionCallArgs();
+                                }
+                               break;
+                       default: return;
+                       }
+               }
+       }
+       SQInteger Factor()
+       {
+               switch(_token)
+               {
+               case TK_STRING_LITERAL: {
+                               //SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1));
+                               _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1)));
+                               Lex(); 
+                       }
+                       break;
+               case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break;
+               case TK_VARGV: { Lex();
+                       Expect(_SC('['));
+                       Expression();
+                       Expect(_SC(']'));
+                       SQInteger src = _fs->PopTarget();
+                       _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src);
+                                          }
+                       break;
+               case TK_IDENTIFIER:
+               case TK_CONSTRUCTOR:
+               case TK_THIS:{
+                       _exst._freevar = false;
+                       SQObject id;
+                               switch(_token) {
+                                       case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break;
+                                       case TK_THIS: id = _fs->CreateString(_SC("this")); break;
+                                       case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break;
+                               }
+                               SQInteger pos = -1;
+                               Lex();
+                               if((pos = _fs->GetLocalVariable(id)) == -1) {
+                                       //checks if is a free variable
+                                       if((pos = _fs->GetOuterVariable(id)) != -1) {
+                                               _exst._deref = _fs->PushTarget();
+                                               _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos);        
+                                               _exst._freevar = true;
+                                       } else {
+                                               _fs->PushTarget(0);
+                                               _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+                                               if(NeedGet()) Emit2ArgsOP(_OP_GET);
+                                               _exst._deref = DEREF_FIELD;
+                                       }
+                               }
+                               else{
+                                       _fs->PushTarget(pos);
+                                       _exst._deref = pos;
+                               }
+                               return _exst._deref;
+                       }
+                       break;
+               case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break;
+               case TK_DOUBLE_COLON:  // "::"
+                       _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget());
+                       _exst._deref = DEREF_FIELD;
+                       _token = _SC('.'); //hack
+                       return -1;
+                       break;
+               case TK_NULL: 
+                       _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
+                       Lex();
+                       break;
+               case TK_INTEGER: {
+                       if((_lex._nvalue & (~0x7FFFFFFF)) == 0) { //does it fit in 32 bits?
+                               _fs->AddInstruction(_OP_LOADINT, _fs->PushTarget(),_lex._nvalue);
+                       }
+                       else {
+                               _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue));
+                       }
+                       Lex();
+                                                }
+                       break;
+               case TK_FLOAT:
+                       if(sizeof(SQFloat) == sizeof(SQInt32)) {
+                               _fs->AddInstruction(_OP_LOADFLOAT, _fs->PushTarget(),*((SQInt32 *)&_lex._fvalue));
+                       }
+                       else {
+                               _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue));
+                       }
+                       Lex();
+                       break;
+               case TK_TRUE: case TK_FALSE:
+                       _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0);
+                       Lex();
+                       break;
+               case _SC('['): {
+                               _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget());
+                               SQInteger apos = _fs->GetCurrentPos(),key = 0;
+                               Lex();
+                               while(_token != _SC(']')) {
+                    Expression(); 
+                                       if(_token == _SC(',')) Lex();
+                                       SQInteger val = _fs->PopTarget();
+                                       SQInteger array = _fs->TopTarget();
+                                       _fs->AddInstruction(_OP_APPENDARRAY, array, val);
+                                       key++;
+                               }
+                               _fs->SetIntructionParam(apos, 1, key);
+                               Lex();
+                       }
+                       break;
+               case _SC('{'):{
+                       _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
+                       Lex();ParseTableOrClass(_SC(','));
+                                }
+                       break;
+               case TK_FUNCTION: FunctionExp(_token);break;
+               case TK_CLASS: Lex(); ClassExp();break;
+               case _SC('-'): UnaryOP(_OP_NEG); break;
+               case _SC('!'): UnaryOP(_OP_NOT); break;
+               case _SC('~'): UnaryOP(_OP_BWNOT); break;
+               case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break;
+               case TK_RESUME : UnaryOP(_OP_RESUME); break;
+               case TK_CLONE : UnaryOP(_OP_CLONE); break;
+               case TK_MINUSMINUS : 
+               case TK_PLUSPLUS :PrefixIncDec(_token); break;
+               case TK_DELETE : DeleteExpr(); break;
+               case TK_DELEGATE : DelegateExpr(); break;
+               case _SC('('): Lex(); CommaExpr(); Expect(_SC(')'));
+                       break;
+               default: Error(_SC("expression expected"));
+               }
+               return -1;
+       }
+       void UnaryOP(SQOpcode op)
+       {
+               Lex(); PrefixedExpr();
+               SQInteger src = _fs->PopTarget();
+               _fs->AddInstruction(op, _fs->PushTarget(), src);
+       }
+       bool NeedGet()
+       {
+               switch(_token) {
+               case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS:
+               case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ:
+                       return false;
+               }
+               return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('[')));
+       }
+       
+       void FunctionCallArgs()
+       {
+               SQInteger nargs = 1;//this
+                while(_token != _SC(')')) {
+                        Expression(true);
+                        MoveIfCurrentTargetIsLocal();
+                        nargs++; 
+                        if(_token == _SC(',')){ 
+                                Lex(); 
+                                if(_token == ')') Error(_SC("expression expected, found ')'"));
+                        }
+                }
+                Lex();
+                for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget();
+                SQInteger stackbase = _fs->PopTarget();
+                SQInteger closure = _fs->PopTarget();
+         _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);
+       }
+       void ParseTableOrClass(SQInteger separator,SQInteger terminator = '}')
+       {
+               SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0;
+               
+               while(_token != terminator) {
+                       bool hasattrs = false;
+                       bool isstatic = false;
+                       //check if is an attribute
+                       if(separator == ';') {
+                               if(_token == TK_ATTR_OPEN) {
+                                       _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex();
+                                       ParseTableOrClass(',',TK_ATTR_CLOSE);
+                                       hasattrs = true;
+                               }
+                               if(_token == TK_STATIC) {
+                                       isstatic = true;
+                                       Lex();
+                               }
+                       }
+                       switch(_token) {
+                               case TK_FUNCTION:
+                               case TK_CONSTRUCTOR:{
+                                       SQInteger tk = _token;
+                                       Lex();
+                                       SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor"));
+                                       Expect(_SC('('));
+                                       _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+                                       CreateFunction(id);
+                                       _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
+                                                                 }
+                                                                 break;
+                               case _SC('['):
+                                       Lex(); CommaExpr(); Expect(_SC(']'));
+                                       Expect(_SC('=')); Expression();
+                                       break;
+                               default :
+                                       _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
+                                       Expect(_SC('=')); Expression();
+                       }
+
+                       if(_token == separator) Lex();//optional comma/semicolon
+                       nkeys++;
+                       SQInteger val = _fs->PopTarget();
+                       SQInteger key = _fs->PopTarget();
+                       SQInteger attrs = hasattrs ? _fs->PopTarget():-1;
+                       assert(hasattrs && attrs == key-1 || !hasattrs);
+                       unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0);
+                       SQInteger table = _fs->TopTarget(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE
+                       _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val);
+                       //_fs->PopTarget();
+               }
+               if(separator == _SC(',')) //hack recognizes a table from the separator
+                       _fs->SetIntructionParam(tpos, 1, nkeys);
+               Lex();
+       }
+       void LocalDeclStatement()
+       {
+               SQObject varname;
+               do {
+                       Lex(); varname = Expect(TK_IDENTIFIER);
+                       if(_token == _SC('=')) {
+                               Lex(); Expression();
+                               SQInteger src = _fs->PopTarget();
+                               SQInteger dest = _fs->PushTarget();
+                               if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src);
+                       }
+                       else{
+                               _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
+                       }
+                       _fs->PopTarget();
+                       _fs->PushLocalVariable(varname);
+               
+               } while(_token == _SC(','));
+       }
+       void IfStatement()
+       {
+               SQInteger jmppos;
+               bool haselse = false;
+               Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+               _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+               SQInteger jnepos = _fs->GetCurrentPos();
+               SQInteger stacksize = _fs->GetStackSize();
+               
+               Statement();
+               //
+               if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon();
+               
+               CleanStack(stacksize);
+               SQInteger endifblock = _fs->GetCurrentPos();
+               if(_token == TK_ELSE){
+                       haselse = true;
+                       stacksize = _fs->GetStackSize();
+                       _fs->AddInstruction(_OP_JMP);
+                       jmppos = _fs->GetCurrentPos();
+                       Lex();
+                       Statement(); OptionalSemicolon();
+                       CleanStack(stacksize);
+                       _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
+               }
+               _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0));
+       }
+       void WhileStatement()
+       {
+               SQInteger jzpos, jmppos;
+               SQInteger stacksize = _fs->GetStackSize();
+               jmppos = _fs->GetCurrentPos();
+               Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+               
+               BEGIN_BREAKBLE_BLOCK();
+               _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+               jzpos = _fs->GetCurrentPos();
+               stacksize = _fs->GetStackSize();
+               
+               Statement();
+               
+               CleanStack(stacksize);
+               _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
+               _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
+               
+               END_BREAKBLE_BLOCK(jmppos);
+       }
+       void DoWhileStatement()
+       {
+               Lex();
+               SQInteger jzpos = _fs->GetCurrentPos();
+               SQInteger stacksize = _fs->GetStackSize();
+               BEGIN_BREAKBLE_BLOCK()
+               Statement();
+               CleanStack(stacksize);
+               Expect(TK_WHILE);
+               SQInteger continuetrg = _fs->GetCurrentPos();
+               Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+               _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1);
+               END_BREAKBLE_BLOCK(continuetrg);
+       }
+       void ForStatement()
+       {
+               Lex();
+               SQInteger stacksize = _fs->GetStackSize();
+               Expect(_SC('('));
+               if(_token == TK_LOCAL) LocalDeclStatement();
+               else if(_token != _SC(';')){
+                       CommaExpr();
+                       _fs->PopTarget();
+               }
+               Expect(_SC(';'));
+               _fs->SnoozeOpt();
+               SQInteger jmppos = _fs->GetCurrentPos();
+               SQInteger jzpos = -1;
+               if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); }
+               Expect(_SC(';'));
+               _fs->SnoozeOpt();
+               SQInteger expstart = _fs->GetCurrentPos() + 1;
+               if(_token != _SC(')')) {
+                       CommaExpr();
+                       _fs->PopTarget();
+               }
+               Expect(_SC(')'));
+               _fs->SnoozeOpt();
+               SQInteger expend = _fs->GetCurrentPos();
+               SQInteger expsize = (expend - expstart) + 1;
+               SQInstructionVec exp;
+               if(expsize > 0) {
+                       for(SQInteger i = 0; i < expsize; i++)
+                               exp.push_back(_fs->GetInstruction(expstart + i));
+                       _fs->PopInstructions(expsize);
+               }
+               BEGIN_BREAKBLE_BLOCK()
+               Statement();
+               SQInteger continuetrg = _fs->GetCurrentPos();
+               if(expsize > 0) {
+                       for(SQInteger i = 0; i < expsize; i++)
+                               _fs->AddInstruction(exp[i]);
+               }
+               _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0);
+               if(jzpos>  0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
+               CleanStack(stacksize);
+               
+               END_BREAKBLE_BLOCK(continuetrg);
+       }
+       void ForEachStatement()
+       {
+               SQObject idxname, valname;
+               Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER);
+               if(_token == _SC(',')) {
+                       idxname = valname;
+                       Lex(); valname = Expect(TK_IDENTIFIER);
+               }
+               else{
+                       idxname = _fs->CreateString(_SC("@INDEX@"));
+               }
+               Expect(TK_IN);
+               
+               //save the stack size
+               SQInteger stacksize = _fs->GetStackSize();
+               //put the table in the stack(evaluate the table expression)
+               Expression(); Expect(_SC(')'));
+               SQInteger container = _fs->TopTarget();
+               //push the index local var
+               SQInteger indexpos = _fs->PushLocalVariable(idxname);
+               _fs->AddInstruction(_OP_LOADNULLS, indexpos,1);
+               //push the value local var
+               SQInteger valuepos = _fs->PushLocalVariable(valname);
+               _fs->AddInstruction(_OP_LOADNULLS, valuepos,1);
+               //push reference index
+               SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible
+               _fs->AddInstruction(_OP_LOADNULLS, itrpos,1);
+               SQInteger jmppos = _fs->GetCurrentPos();
+               _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos);
+               SQInteger foreachpos = _fs->GetCurrentPos();
+               _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos);
+               //generate the statement code
+               BEGIN_BREAKBLE_BLOCK()
+               Statement();
+               _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
+               _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos);
+               _fs->SetIntructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos);
+               //restore the local variable stack(remove index,val and ref idx)
+               CleanStack(stacksize);
+               END_BREAKBLE_BLOCK(foreachpos - 1);
+       }
+       void SwitchStatement()
+       {
+               Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+               Expect(_SC('{'));
+               SQInteger expr = _fs->TopTarget();
+               bool bfirst = true;
+               SQInteger tonextcondjmp = -1;
+               SQInteger skipcondjmp = -1;
+               SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size();
+               _fs->_breaktargets.push_back(0);
+               while(_token == TK_CASE) {
+                       //_fs->AddLineInfos(_lex._currentline, _lineinfo); think about this one
+                       if(!bfirst) {
+                               _fs->AddInstruction(_OP_JMP, 0, 0);
+                               skipcondjmp = _fs->GetCurrentPos();
+                               _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
+                       }
+                       //condition
+                       Lex(); Expression(); Expect(_SC(':'));
+                       SQInteger trg = _fs->PopTarget();
+                       _fs->AddInstruction(_OP_EQ, trg, trg, expr);
+                       _fs->AddInstruction(_OP_JZ, trg, 0);
+                       //end condition
+                       if(skipcondjmp != -1) {
+                               _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp));
+                       }
+                       tonextcondjmp = _fs->GetCurrentPos();
+                       SQInteger stacksize = _fs->GetStackSize();
+                       Statements();
+                       _fs->SetStackSize(stacksize);
+                       bfirst = false;
+               }
+               if(tonextcondjmp != -1)
+                       _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
+               if(_token == TK_DEFAULT) {
+               //      _fs->AddLineInfos(_lex._currentline, _lineinfo);
+                       Lex(); Expect(_SC(':'));
+                       SQInteger stacksize = _fs->GetStackSize();
+                       Statements();
+                       _fs->SetStackSize(stacksize);
+               }
+               Expect(_SC('}'));
+               _fs->PopTarget();
+               __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;
+               if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__);
+               _fs->_breaktargets.pop_back();
+               
+       }
+       void FunctionStatement()
+       {
+               SQObject id;
+               Lex(); id = Expect(TK_IDENTIFIER);
+               _fs->PushTarget(0);
+               _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+               if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
+               
+               while(_token == TK_DOUBLE_COLON) {
+                       Lex();
+                       id = Expect(TK_IDENTIFIER);
+                       _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+                       if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
+               }
+               Expect(_SC('('));
+               CreateFunction(id);
+               _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
+               EmitDerefOp(_OP_NEWSLOT);
+               _fs->PopTarget();
+       }
+       void ClassStatement()
+       {
+               ExpState es;
+               Lex(); PushExpState();
+               _exst._class_or_delete = true;
+               _exst._funcarg = false;
+               PrefixedExpr();
+               es = PopExpState();
+               if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name"));
+               if(es._deref == DEREF_FIELD) {
+                       ClassExp();
+                       EmitDerefOp(_OP_NEWSLOT);
+                       _fs->PopTarget();
+               }
+               else Error(_SC("cannot create a class in a local with the syntax(class <local>)"));
+       }
+       void TryCatchStatement()
+       {
+               SQObject exid;
+               Lex();
+               _fs->AddInstruction(_OP_PUSHTRAP,0,0);
+               _fs->_traps++;
+               if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++;
+               if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++;
+               SQInteger trappos = _fs->GetCurrentPos();
+               Statement();
+               _fs->_traps--;
+               _fs->AddInstruction(_OP_POPTRAP, 1, 0);
+               if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--;
+               if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--;
+               _fs->AddInstruction(_OP_JMP, 0, 0);
+               SQInteger jmppos = _fs->GetCurrentPos();
+               _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos));
+               Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')'));
+               SQInteger stacksize = _fs->GetStackSize();
+               SQInteger ex_target = _fs->PushLocalVariable(exid);
+               _fs->SetIntructionParam(trappos, 0, ex_target);
+               Statement();
+               _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0);
+               CleanStack(stacksize);
+       }
+       void FunctionExp(SQInteger ftype)
+       {
+               Lex(); Expect(_SC('('));
+               CreateFunction(_null_);
+               _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1);
+       }
+       void ClassExp()
+       {
+               SQInteger base = -1;
+               SQInteger attrs = -1;
+               if(_token == TK_EXTENDS) {
+                       Lex(); Expression();
+                       base = _fs->TopTarget();
+               }
+               if(_token == TK_ATTR_OPEN) {
+                       Lex();
+                       _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
+                       ParseTableOrClass(_SC(','),TK_ATTR_CLOSE);
+                       attrs = _fs->TopTarget();
+               }
+               Expect(_SC('{'));
+               if(attrs != -1) _fs->PopTarget();
+               if(base != -1) _fs->PopTarget();
+               _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs);
+               ParseTableOrClass(_SC(';'));
+       }
+       void DelegateExpr()
+       {
+               Lex(); CommaExpr();
+               Expect(_SC(':'));
+               CommaExpr();
+               SQInteger table = _fs->PopTarget(), delegate = _fs->PopTarget();
+               _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate);
+       }
+       void DeleteExpr()
+       {
+               ExpState es;
+               Lex(); PushExpState();
+               _exst._class_or_delete = true;
+               _exst._funcarg = false;
+               PrefixedExpr();
+               es = PopExpState();
+               if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression"));
+               if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE);
+               else Error(_SC("cannot delete a local"));
+       }
+       void PrefixIncDec(SQInteger token)
+       {
+               ExpState es;
+               Lex(); PushExpState();
+               _exst._class_or_delete = true;
+               _exst._funcarg = false;
+               PrefixedExpr();
+               es = PopExpState();
+               if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1);
+               else {
+                       SQInteger src = _fs->PopTarget();
+                       _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1);
+               }
+       }
+       void CreateFunction(SQObject &name)
+       {
+               
+               SQFuncState *funcstate = _fs->PushChildState(_ss(_vm));
+               funcstate->_name = name;
+               SQObject paramname;
+               funcstate->AddParameter(_fs->CreateString(_SC("this")));
+               funcstate->_sourcename = _sourcename;
+               while(_token!=_SC(')')) {
+                       if(_token == TK_VARPARAMS) {
+                               funcstate->_varparams = true;
+                               Lex();
+                               if(_token != _SC(')')) Error(_SC("expected ')'"));
+                               break;
+                       }
+                       else {
+                               paramname = Expect(TK_IDENTIFIER);
+                               funcstate->AddParameter(paramname);
+                               if(_token == _SC(',')) Lex();
+                               else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
+                       }
+               }
+               Expect(_SC(')'));
+               //outer values
+               if(_token == _SC(':')) {
+                       Lex(); Expect(_SC('('));
+                       while(_token != _SC(')')) {
+                               paramname = Expect(TK_IDENTIFIER);
+                               //outers are treated as implicit local variables
+                               funcstate->AddOuterValue(paramname);
+                               if(_token == _SC(',')) Lex();
+                               else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
+                       }
+                       Lex();
+               }
+               
+               SQFuncState *currchunk = _fs;
+               _fs = funcstate;
+               Statement();
+               funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true);
+        funcstate->AddInstruction(_OP_RETURN, -1);
+               funcstate->SetStackSize(0);
+               //_fs->->_stacksize = _fs->_stacksize;
+               SQFunctionProto *func = funcstate->BuildProto();
+#ifdef _DEBUG_DUMP
+               funcstate->Dump(func);
+#endif
+               _fs = currchunk;
+               _fs->_functions.push_back(func);
+               _fs->PopChildState();
+       }
+       void CleanStack(SQInteger stacksize)
+       {
+               if(_fs->GetStackSize() != stacksize)
+                       _fs->SetStackSize(stacksize);
+       }
+       void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve)
+       {
+               while(ntoresolve > 0) {
+                       SQInteger pos = funcstate->_unresolvedbreaks.back();
+                       funcstate->_unresolvedbreaks.pop_back();
+                       //set the jmp instruction
+                       funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0);
+                       ntoresolve--;
+               }
+       }
+       void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos)
+       {
+               while(ntoresolve > 0) {
+                       SQInteger pos = funcstate->_unresolvedcontinues.back();
+                       funcstate->_unresolvedcontinues.pop_back();
+                       //set the jmp instruction
+                       funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0);
+                       ntoresolve--;
+               }
+       }
+private:
+       SQInteger _token;
+       SQFuncState *_fs;
+       SQObjectPtr _sourcename;
+       SQLexer _lex;
+       bool _lineinfo;
+       bool _raiseerror;
+       SQInteger _debugline;
+       SQInteger _debugop;
+       ExpStateVec _expstates;
+       SQChar *compilererror;
+       jmp_buf _errorjmp;
+       SQVM *_vm;
+};
+
+bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)
+{
+       SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);
+       return p.Compile(out);
+}
diff --git a/src/squirrel/squirrel/sqcompiler.h b/src/squirrel/squirrel/sqcompiler.h
new file mode 100644 (file)
index 0000000..ed2a89b
--- /dev/null
@@ -0,0 +1,75 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQCOMPILER_H_
+#define _SQCOMPILER_H_
+
+struct SQVM;
+
+#define        TK_IDENTIFIER   258
+#define        TK_STRING_LITERAL       259
+#define        TK_INTEGER      260
+#define        TK_FLOAT        261
+#define        TK_DELEGATE     262
+#define        TK_DELETE       263
+#define        TK_EQ   264
+#define        TK_NE   265
+#define        TK_LE   266
+#define        TK_GE   267
+#define        TK_SWITCH       268
+#define        TK_ARROW        269
+#define        TK_AND  270
+#define        TK_OR   271
+#define        TK_IF   272
+#define        TK_ELSE 273
+#define        TK_WHILE        274
+#define        TK_BREAK        275
+#define        TK_FOR  276
+#define        TK_DO   277
+#define        TK_NULL 278
+#define        TK_FOREACH      279
+#define        TK_IN   280
+#define        TK_NEWSLOT      281
+#define        TK_MODULO       282
+#define        TK_LOCAL        283
+#define        TK_CLONE        284
+#define        TK_FUNCTION     285
+#define        TK_RETURN       286
+#define        TK_TYPEOF       287
+#define        TK_UMINUS       288
+#define        TK_PLUSEQ       289
+#define        TK_MINUSEQ      290
+#define        TK_CONTINUE     291
+#define TK_YIELD 292
+#define TK_TRY 293
+#define TK_CATCH 294
+#define TK_THROW 295
+#define TK_SHIFTL 296
+#define TK_SHIFTR 297
+#define TK_RESUME 298
+#define TK_DOUBLE_COLON 299
+#define TK_CASE 300
+#define TK_DEFAULT 301
+#define TK_THIS 302
+#define TK_PLUSPLUS 303
+#define TK_MINUSMINUS 304
+#define TK_PARENT 305
+#define TK_USHIFTR 306
+#define TK_CLASS 307
+#define TK_EXTENDS 308
+#define TK_CONSTRUCTOR 310
+#define TK_INSTANCEOF 311
+#define TK_VARPARAMS 312
+#define TK_VARGC 313
+#define TK_VARGV 314
+#define TK_TRUE 315
+#define TK_FALSE 316
+#define TK_MULEQ 317
+#define TK_DIVEQ 318
+#define TK_MODEQ 319
+#define TK_ATTR_OPEN 320
+#define TK_ATTR_CLOSE 321
+#define TK_STATIC 322
+
+
+typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s);
+bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo);
+#endif //_SQCOMPILER_H_
diff --git a/src/squirrel/squirrel/sqdebug.cpp b/src/squirrel/squirrel/sqdebug.cpp
new file mode 100644 (file)
index 0000000..40e0c9b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <stdarg.h>
+#include "sqvm.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+
+SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si)
+{
+       SQInteger cssize = v->_callsstacksize;
+       if (cssize > level) {
+               memset(si, 0, sizeof(SQStackInfos));
+               SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
+               switch (type(ci._closure)) {
+               case OT_CLOSURE:{
+                       SQFunctionProto *func = _funcproto(_closure(ci._closure)->_function);
+                       if (type(func->_name) == OT_STRING)
+                               si->funcname = _stringval(func->_name);
+                       if (type(func->_sourcename) == OT_STRING)
+                               si->source = _stringval(func->_sourcename);
+                       si->line = func->GetLine(ci._ip);
+                                               }
+                       break;
+               case OT_NATIVECLOSURE:
+                       si->source = _SC("NATIVE");
+                       si->funcname = _SC("unknown");
+                       if(type(_nativeclosure(ci._closure)->_name) == OT_STRING)
+                               si->funcname = _stringval(_nativeclosure(ci._closure)->_name);
+                       si->line = -1;
+                       break;
+               default: break; //shutup compiler
+               }
+               return SQ_OK;
+       }
+       return SQ_ERROR;
+}
+
+void SQVM::Raise_Error(const SQChar *s, ...)
+{
+       va_list vl;
+       va_start(vl, s);
+       scvsprintf(_sp(rsl((SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2))), s, vl);
+       va_end(vl);
+       _lasterror = SQString::Create(_ss(this),_spval,-1);
+}
+
+void SQVM::Raise_Error(SQObjectPtr &desc)
+{
+       _lasterror = desc;
+}
+
+SQString *SQVM::PrintObjVal(const SQObject &o)
+{
+       switch(type(o)) {
+       case OT_STRING: return _string(o);
+       case OT_INTEGER:
+               scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)), _SC("%d"), _integer(o));
+               return SQString::Create(_ss(this), _spval);
+               break;
+       case OT_FLOAT:
+               scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)), _SC("%.14g"), _float(o));
+               return SQString::Create(_ss(this), _spval);
+               break;
+       default:
+               return SQString::Create(_ss(this), GetTypeName(o));
+       }
+}
+
+void SQVM::Raise_IdxError(SQObject &o)
+{
+       SQObjectPtr oval = PrintObjVal(o);
+       Raise_Error(_SC("the index '%.50s' does not exist"), _stringval(oval));
+}
+
+void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2)
+{
+       SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2);
+       Raise_Error(_SC("comparsion between '%.50s' and '%.50s'"), _stringval(oval1), _stringval(oval2));
+}
+
+
+void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type)
+{
+       SQObjectPtr exptypes = SQString::Create(_ss(this), _SC(""), -1);
+       SQInteger found = 0;    
+       for(SQInteger i=0; i<16; i++)
+       {
+               SQInteger mask = 0x00000001 << i;
+               if(typemask & (mask)) {
+                       if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes);
+                       found ++;
+                       StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes);
+               }
+       }
+       Raise_Error(_SC("parameter %d has an invalid type '%s' ; expected: '%s'"), nparam, IdType2Name((SQObjectType)type), _stringval(exptypes));
+}
diff --git a/src/squirrel/squirrel/sqfuncproto.h b/src/squirrel/squirrel/sqfuncproto.h
new file mode 100644 (file)
index 0000000..367b818
--- /dev/null
@@ -0,0 +1,155 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQFUNCTION_H_
+#define _SQFUNCTION_H_
+
+#include "sqopcodes.h"
+
+enum SQOuterType {
+       otLOCAL = 0,
+       otSYMBOL = 1,
+       otOUTER = 2
+};
+
+struct SQOuterVar
+{
+       
+       SQOuterVar(){}
+       SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t)
+       {
+               _name = name;
+               _src=src;
+               _type=t;
+       }
+       SQOuterVar(const SQOuterVar &ov)
+       {
+               _type=ov._type;
+               _src=ov._src;
+               _name=ov._name;
+       }
+       SQOuterType _type;
+       SQObjectPtr _name;
+       SQObjectPtr _src;
+};
+
+struct SQLocalVarInfo
+{
+       SQLocalVarInfo():_start_op(0),_end_op(0){}
+       SQLocalVarInfo(const SQLocalVarInfo &lvi)
+       {
+               _name=lvi._name;
+               _start_op=lvi._start_op;
+               _end_op=lvi._end_op;
+               _pos=lvi._pos;
+       }
+       SQObjectPtr _name;
+       SQUnsignedInteger _start_op;
+       SQUnsignedInteger _end_op;
+       SQUnsignedInteger _pos;
+};
+
+struct SQLineInfo { SQInteger _line;SQInteger _op; };
+
+typedef sqvector<SQOuterVar> SQOuterVarVec;
+typedef sqvector<SQLocalVarInfo> SQLocalVarInfoVec;
+typedef sqvector<SQLineInfo> SQLineInfoVec;
+
+#define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf) (sizeof(SQFunctionProto) \
+               +((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \
+               +(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \
+               +(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \
+               +(localinf*sizeof(SQLocalVarInfo)))
+
+#define _CONSTRUCT_VECTOR(type,size,ptr) { \
+       for(SQInteger n = 0; n < size; n++) { \
+                       new (&ptr[n]) type(); \
+               } \
+}
+
+#define _DESTRUCT_VECTOR(type,size,ptr) { \
+       for(SQInteger nl = 0; nl < size; nl++) { \
+                       ptr[nl].~type(); \
+       } \
+}
+struct SQFunctionProto : public SQRefCounted
+{
+private:
+       SQFunctionProto(){
+       _stacksize=0;
+       _bgenerator=false;}
+public:
+       static SQFunctionProto *Create(SQInteger ninstructions,
+               SQInteger nliterals,SQInteger nparameters,
+               SQInteger nfunctions,SQInteger noutervalues,
+               SQInteger nlineinfos,SQInteger nlocalvarinfos)
+       {
+               SQFunctionProto *f;
+               //I compact the whole class and members in a single memory allocation
+               f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos));
+               new (f) SQFunctionProto;
+               f->_ninstructions = ninstructions;
+               f->_literals = (SQObjectPtr*)&f->_instructions[ninstructions];
+               f->_nliterals = nliterals;
+               f->_parameters = (SQObjectPtr*)&f->_literals[nliterals];
+               f->_nparameters = nparameters;
+               f->_functions = (SQObjectPtr*)&f->_parameters[nparameters];
+               f->_nfunctions = nfunctions;
+               f->_outervalues = (SQOuterVar*)&f->_functions[nfunctions];
+               f->_noutervalues = noutervalues;
+               f->_lineinfos = (SQLineInfo *)&f->_outervalues[noutervalues];
+               f->_nlineinfos = nlineinfos;
+               f->_localvarinfos = (SQLocalVarInfo *)&f->_lineinfos[nlineinfos];
+               f->_nlocalvarinfos = nlocalvarinfos;
+
+               _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals);
+               _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters);
+               _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions);
+               _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues);
+               //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers
+               _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos);
+               return f;
+       }
+       void Release(){ 
+               _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals);
+               _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters);
+               _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions);
+               _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues);
+               //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers
+               _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos);
+               SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos);
+               this->~SQFunctionProto();
+               sq_vm_free(this,size);
+       }
+       const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop);
+       SQInteger GetLine(SQInstruction *curr);
+       bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
+       static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
+
+       SQObjectPtr _sourcename;
+       SQObjectPtr _name;
+    SQInteger _stacksize;
+       bool _bgenerator;
+       bool _varparams;
+
+       SQInteger _nlocalvarinfos;
+       SQLocalVarInfo *_localvarinfos;
+
+       SQInteger _nlineinfos;
+       SQLineInfo *_lineinfos;
+
+       SQInteger _nliterals;
+       SQObjectPtr *_literals;
+
+       SQInteger _nparameters;
+       SQObjectPtr *_parameters;
+       
+       SQInteger _nfunctions;
+       SQObjectPtr *_functions;
+
+       SQInteger _noutervalues;
+       SQOuterVar *_outervalues;
+       
+       SQInteger _ninstructions;
+       SQInstruction _instructions[1];
+};
+
+#endif //_SQFUNCTION_H_
diff --git a/src/squirrel/squirrel/sqfuncstate.cpp b/src/squirrel/squirrel/sqfuncstate.cpp
new file mode 100644 (file)
index 0000000..962a6cd
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqcompiler.h"
+#include "sqfuncproto.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqopcodes.h"
+#include "sqfuncstate.h"
+
+#ifdef _DEBUG_DUMP
+SQInstructionDesc g_InstrDesc[]={
+       {_SC("_OP_LINE")},
+       {_SC("_OP_LOAD")},
+       {_SC("_OP_LOADINT")},
+       {_SC("_OP_LOADFLOAT")},
+       {_SC("_OP_DLOAD")},
+       {_SC("_OP_TAILCALL")},
+       {_SC("_OP_CALL")},
+       {_SC("_OP_PREPCALL")},
+       {_SC("_OP_PREPCALLK")},
+       {_SC("_OP_GETK")},
+       {_SC("_OP_MOVE")},
+       {_SC("_OP_NEWSLOT")},
+       {_SC("_OP_DELETE")},
+       {_SC("_OP_SET")},
+       {_SC("_OP_GET")},
+       {_SC("_OP_EQ")},
+       {_SC("_OP_NE")},
+       {_SC("_OP_ARITH")},
+       {_SC("_OP_BITW")},
+       {_SC("_OP_RETURN")},
+       {_SC("_OP_LOADNULLS")},
+       {_SC("_OP_LOADROOTTABLE")},
+       {_SC("_OP_LOADBOOL")},
+       {_SC("_OP_DMOVE")},
+       {_SC("_OP_JMP")},
+       {_SC("_OP_JNZ")},
+       {_SC("_OP_JZ")},
+       {_SC("_OP_LOADFREEVAR")},
+       {_SC("_OP_VARGC")},
+       {_SC("_OP_GETVARGV")},
+       {_SC("_OP_NEWTABLE")},
+       {_SC("_OP_NEWARRAY")},
+       {_SC("_OP_APPENDARRAY")},
+       {_SC("_OP_GETPARENT")},
+       {_SC("_OP_COMPARITH")},
+       {_SC("_OP_COMPARITHL")},
+       {_SC("_OP_INC")},
+       {_SC("_OP_INCL")},
+       {_SC("_OP_PINC")},
+       {_SC("_OP_PINCL")},
+       {_SC("_OP_CMP")},
+       {_SC("_OP_EXISTS")},
+       {_SC("_OP_INSTANCEOF")},
+       {_SC("_OP_AND")},
+       {_SC("_OP_OR")},
+       {_SC("_OP_NEG")},
+       {_SC("_OP_NOT")},
+       {_SC("_OP_BWNOT")},
+       {_SC("_OP_CLOSURE")},
+       {_SC("_OP_YIELD")},
+       {_SC("_OP_RESUME")},
+       {_SC("_OP_FOREACH")},
+       {_SC("_OP_POSTFOREACH")},
+       {_SC("_OP_DELEGATE")},
+       {_SC("_OP_CLONE")},
+       {_SC("_OP_TYPEOF")},
+       {_SC("_OP_PUSHTRAP")},
+       {_SC("_OP_POPTRAP")},
+       {_SC("_OP_THROW")},
+       {_SC("_OP_CLASS")},
+       {_SC("_OP_NEWSLOTA")}
+};
+#endif
+void DumpLiteral(SQObjectPtr &o)
+{
+       switch(type(o)){
+               case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break;
+               case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break;
+               case OT_INTEGER: scprintf(_SC("{%d}"),_integer(o));break;
+               default: assert(0); break; //shut up compiler
+       }
+}
+
+SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed)
+{
+               _nliterals = 0;
+               _literals = SQTable::Create(ss,0);
+               _strings =  SQTable::Create(ss,0);
+               _sharedstate = ss;
+               _lastline = 0;
+               _optimization = true;
+               _parent = parent;
+               _stacksize = 0;
+               _traps = 0;
+               _returnexp = 0;
+               _varparams = false;
+               _errfunc = efunc;
+               _errtarget = ed;
+               _bgenerator = false;
+
+}
+
+void SQFuncState::Error(const SQChar *err)
+{
+       _errfunc(_errtarget,err);
+}
+
+#ifdef _DEBUG_DUMP
+void SQFuncState::Dump(SQFunctionProto *func)
+{
+       SQUnsignedInteger n=0,i;
+       SQInteger si;
+       scprintf(_SC("SQInstruction sizeof %d\n"),sizeof(SQInstruction));
+       scprintf(_SC("SQObject sizeof %d\n"),sizeof(SQObject));
+       scprintf(_SC("--------------------------------------------------------------------\n"));
+       scprintf(_SC("*****FUNCTION [%s]\n"),type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown"));
+       scprintf(_SC("-----LITERALS\n"));
+       SQObjectPtr refidx,key,val;
+       SQInteger idx;
+       SQObjectPtrVec templiterals;
+       templiterals.resize(_nliterals);
+       while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
+               refidx=idx;
+               templiterals[_integer(val)]=key;
+       }
+       for(i=0;i<templiterals.size();i++){
+               scprintf(_SC("[%d] "),n);
+               DumpLiteral(templiterals[i]);
+               scprintf(_SC("\n"));
+               n++;
+       }
+       scprintf(_SC("-----PARAMS\n"));
+       if(_varparams)
+               scprintf(_SC("<<VARPARAMS>>\n"));
+       n=0;
+       for(i=0;i<_parameters.size();i++){
+               scprintf(_SC("[%d] "),n);
+               DumpLiteral(_parameters[i]);
+               scprintf(_SC("\n"));
+               n++;
+       }
+       scprintf(_SC("-----LOCALS\n"));
+       for(si=0;si<func->_nlocalvarinfos;si++){
+               SQLocalVarInfo lvi=func->_localvarinfos[si];
+               scprintf(_SC("[%d] %s \t%d %d\n"),lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op);
+               n++;
+       }
+       scprintf(_SC("-----LINE INFO\n"));
+       for(i=0;i<_lineinfos.size();i++){
+               SQLineInfo li=_lineinfos[i];
+               scprintf(_SC("op [%d] line [%d] \n"),li._op,li._line);
+               n++;
+       }
+       scprintf(_SC("-----dump\n"));
+       n=0;
+       for(i=0;i<_instructions.size();i++){
+               SQInstruction &inst=_instructions[i];
+               if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){
+                       
+                       SQInteger lidx = inst._arg1;
+                       scprintf(_SC("[%03d] %15s %d "),n,g_InstrDesc[inst.op].name,inst._arg0);
+                       if(lidx >= 0xFFFFFFFF)
+                               scprintf(_SC("null"));
+                       else {
+                               SQInteger refidx;
+                               SQObjectPtr val,key,refo;
+                               while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
+                                       refo = refidx;  
+                               }
+                               DumpLiteral(key);
+                       }
+                       if(inst.op != _OP_DLOAD) {
+                               scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3);
+                       }
+                       else {
+                               scprintf(_SC(" %d "),inst._arg2);
+                               lidx = inst._arg3;
+                               if(lidx >= 0xFFFFFFFF)
+                                       scprintf(_SC("null"));
+                               else {
+                                       SQInteger refidx;
+                                       SQObjectPtr val,key,refo;
+                                       while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
+                                               refo = refidx;  
+                               }
+                               DumpLiteral(key);
+                               scprintf(_SC("\n"));
+                       }
+                       }
+               }
+               else if(inst.op==_OP_LOADFLOAT) {
+                       scprintf(_SC("[%03d] %15s %d %f %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3);
+               }
+               else if(inst.op==_OP_ARITH){
+                       scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
+               }
+               else 
+                       scprintf(_SC("[%03d] %15s %d %d %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
+               n++;
+       }
+       scprintf(_SC("-----\n"));
+       scprintf(_SC("stack size[%d]\n"),func->_stacksize);
+       scprintf(_SC("--------------------------------------------------------------------\n\n"));
+}
+#endif
+
+SQInteger SQFuncState::GetNumericConstant(const SQInteger cons)
+{
+       return GetConstant(SQObjectPtr(cons));
+}
+
+SQInteger SQFuncState::GetNumericConstant(const SQFloat cons)
+{
+       return GetConstant(SQObjectPtr(cons));
+}
+
+SQInteger SQFuncState::GetConstant(const SQObject &cons)
+{
+       SQObjectPtr val;
+       if(!_table(_literals)->Get(cons,val))
+       {
+               val = _nliterals;
+               _table(_literals)->NewSlot(cons,val);
+               _nliterals++;
+               if(_nliterals > MAX_LITERALS) {
+                       val.Null();
+                       Error(_SC("internal compiler error: too many literals"));
+               }
+       }
+       return _integer(val);
+}
+
+void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3)
+{
+       _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0);
+       _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1);
+       _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2);
+       _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3);
+}
+
+void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val)
+{
+       switch(arg){
+               case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break;
+               case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break;
+               case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break;
+               case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break;
+       };
+}
+
+SQInteger SQFuncState::AllocStackPos()
+{
+       SQInteger npos=_vlocals.size();
+       _vlocals.push_back(SQLocalVarInfo());
+       if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) {
+               if(_stacksize>MAX_FUNC_STACKSIZE) Error(_SC("internal compiler error: too many locals"));
+               _stacksize=_vlocals.size();
+       }
+       return npos;
+}
+
+SQInteger SQFuncState::PushTarget(SQInteger n)
+{
+       if(n!=-1){
+               _targetstack.push_back(n);
+               return n;
+       }
+       n=AllocStackPos();
+       _targetstack.push_back(n);
+       return n;
+}
+
+SQInteger SQFuncState::GetUpTarget(SQInteger n){
+       return _targetstack[((_targetstack.size()-1)-n)];
+}
+
+SQInteger SQFuncState::TopTarget(){
+       return _targetstack.back();
+}
+SQInteger SQFuncState::PopTarget()
+{
+       SQInteger npos=_targetstack.back();
+       SQLocalVarInfo t=_vlocals[_targetstack.back()];
+       if(type(t._name)==OT_NULL){
+               _vlocals.pop_back();
+       }
+       _targetstack.pop_back();
+       return npos;
+}
+
+SQInteger SQFuncState::GetStackSize()
+{
+       return _vlocals.size();
+}
+
+void SQFuncState::SetStackSize(SQInteger n)
+{
+       SQInteger size=_vlocals.size();
+       while(size>n){
+               size--;
+               SQLocalVarInfo lvi=_vlocals.back();
+               if(type(lvi._name)!=OT_NULL){
+                       lvi._end_op=GetCurrentPos();
+                       _localvarinfos.push_back(lvi);
+               }
+               _vlocals.pop_back();
+       }
+}
+
+bool SQFuncState::IsLocal(SQUnsignedInteger stkpos)
+{
+       if(stkpos>=_vlocals.size())return false;
+       else if(type(_vlocals[stkpos]._name)!=OT_NULL)return true;
+       return false;
+}
+
+SQInteger SQFuncState::PushLocalVariable(const SQObject &name)
+{
+       SQInteger pos=_vlocals.size();
+       SQLocalVarInfo lvi;
+       lvi._name=name;
+       lvi._start_op=GetCurrentPos()+1;
+       lvi._pos=_vlocals.size();
+       _vlocals.push_back(lvi);
+       if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size();
+       
+       return pos;
+}
+
+SQInteger SQFuncState::GetLocalVariable(const SQObject &name)
+{
+       SQInteger locals=_vlocals.size();
+       while(locals>=1){
+               if(type(_vlocals[locals-1]._name)==OT_STRING && _string(_vlocals[locals-1]._name)==_string(name)){
+                       return locals-1;
+               }
+               locals--;
+       }
+       return -1;
+}
+
+SQInteger SQFuncState::GetOuterVariable(const SQObject &name)
+{
+       SQInteger outers = _outervalues.size();
+       for(SQInteger i = 0; i<outers; i++) {
+               if(_string(_outervalues[i]._name) == _string(name))
+                       return i;
+       }
+       return -1;
+}
+
+void SQFuncState::AddOuterValue(const SQObject &name)
+{
+       SQInteger pos=-1;
+       if(_parent) { 
+               pos = _parent->GetLocalVariable(name);
+               if(pos == -1) {
+                       pos = _parent->GetOuterVariable(name);
+                       if(pos != -1) {
+                               _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local
+                               return;
+                       }
+               }
+               else {
+                       _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local
+                       return;
+               }
+       }       
+       _outervalues.push_back(SQOuterVar(name,name,otSYMBOL)); //global
+}
+
+void SQFuncState::AddParameter(const SQObject &name)
+{
+       PushLocalVariable(name);
+       _parameters.push_back(name);
+}
+
+void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force)
+{
+       if(_lastline!=line || force){
+               SQLineInfo li;
+               li._line=line;li._op=(GetCurrentPos()+1);
+               if(lineop)AddInstruction(_OP_LINE,0,line);
+               _lineinfos.push_back(li);
+               _lastline=line;
+       }
+}
+
+void SQFuncState::AddInstruction(SQInstruction &i)
+{
+       SQInteger size = _instructions.size();
+       if(size > 0 && _optimization){ //simple optimizer
+               SQInstruction &pi = _instructions[size-1];//previous instruction
+               switch(i.op) {
+               case _OP_RETURN:
+                       if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) {
+                               pi.op = _OP_TAILCALL;
+                       }
+               break;
+               case _OP_GET:
+                       if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){
+                               pi._arg1 = pi._arg1;
+                               pi._arg2 = (unsigned char)i._arg1;
+                               pi.op = _OP_GETK;
+                               pi._arg0 = i._arg0;
+                               
+                               return;
+                       }
+               break;
+               case _OP_PREPCALL:
+                       if( pi.op == _OP_LOAD  && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
+                               pi.op = _OP_PREPCALLK;
+                               pi._arg0 = i._arg0;
+                               pi._arg1 = pi._arg1;
+                               pi._arg2 = i._arg2;
+                               pi._arg3 = i._arg3;
+                               return;
+                       }
+                       break;
+               case _OP_APPENDARRAY:
+                       if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
+                               pi.op = _OP_APPENDARRAY;
+                               pi._arg0 = i._arg0;
+                               pi._arg1 = pi._arg1;
+                               pi._arg2 = MAX_FUNC_STACKSIZE;
+                               pi._arg3 = MAX_FUNC_STACKSIZE;
+                               return;
+                       }
+                       break;
+               case _OP_MOVE: 
+                       if((pi.op == _OP_GET || pi.op == _OP_ARITH || pi.op == _OP_BITW) && (pi._arg0 == i._arg1))
+                       {
+                               pi._arg0 = i._arg0;
+                               _optimization = false;
+                               return;
+                       }
+
+                       if(pi.op == _OP_MOVE)
+                       {
+                               pi.op = _OP_DMOVE;
+                               pi._arg2 = i._arg0;
+                               pi._arg3 = (unsigned char)i._arg1;
+                               return;
+                       }
+                       break;
+               case _OP_LOAD:
+                       if(pi.op == _OP_LOAD && i._arg1 < 256) {
+                               pi.op = _OP_DLOAD;
+                               pi._arg2 = i._arg0;
+                               pi._arg3 = (unsigned char)i._arg1;
+                               return;
+                       }
+                       break;
+               case _OP_EQ:case _OP_NE:
+                       if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) ))
+                       {
+                               pi.op = i.op;
+                               pi._arg0 = i._arg0;
+                               pi._arg1 = pi._arg1;
+                               pi._arg2 = i._arg2;
+                               pi._arg3 = MAX_FUNC_STACKSIZE;
+                               return;
+                       }
+                       break;
+               case _OP_LOADNULLS:
+                       if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) {
+                               
+                               pi._arg1 = pi._arg1 + 1;
+                               pi.op = _OP_LOADNULLS;
+                               return;
+                       }
+            break;
+               case _OP_LINE:
+                       if(pi.op == _OP_LINE) {
+                               _instructions.pop_back();
+                               _lineinfos.pop_back();
+                       }
+                       break;
+               }
+       }
+       _optimization = true;
+       _instructions.push_back(i);
+}
+
+SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len)
+{
+       SQObjectPtr ns(SQString::Create(_sharedstate,s,len));
+       _table(_strings)->NewSlot(ns,(SQInteger)1);
+       return ns;
+}
+
+SQFunctionProto *SQFuncState::BuildProto()
+{
+       SQFunctionProto *f=SQFunctionProto::Create(_instructions.size(),
+               _nliterals,_parameters.size(),_functions.size(),_outervalues.size(),
+               _lineinfos.size(),_localvarinfos.size());
+
+       SQObjectPtr refidx,key,val;
+       SQInteger idx;
+
+       f->_stacksize = _stacksize;
+       f->_sourcename = _sourcename;
+       f->_bgenerator = _bgenerator;
+       f->_name = _name;
+
+       while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
+               f->_literals[_integer(val)]=key;
+               refidx=idx;
+       }
+
+       for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf];
+       for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np];
+       for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no];
+       for(SQUnsignedInteger no = 0; no < _localvarinfos.size(); no++) f->_localvarinfos[no] = _localvarinfos[no];
+       for(SQUnsignedInteger no = 0; no < _lineinfos.size(); no++) f->_lineinfos[no] = _lineinfos[no];
+
+       memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction));
+
+       f->_varparams = _varparams;
+
+       return f;
+}
+
+SQFuncState *SQFuncState::PushChildState(SQSharedState *ss)
+{
+       SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState));
+       new (child) SQFuncState(ss,this,_errfunc,_errtarget);
+       _childstates.push_back(child);
+       return child;
+}
+
+void SQFuncState::PopChildState()
+{
+       SQFuncState *child = _childstates.back();
+       sq_delete(child,SQFuncState);
+       _childstates.pop_back();
+}
+
+SQFuncState::~SQFuncState()
+{
+       while(_childstates.size() > 0)
+       {
+               PopChildState();
+       }
+}
diff --git a/src/squirrel/squirrel/sqfuncstate.h b/src/squirrel/squirrel/sqfuncstate.h
new file mode 100644 (file)
index 0000000..96eb552
--- /dev/null
@@ -0,0 +1,80 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQFUNCSTATE_H_
+#define _SQFUNCSTATE_H_
+///////////////////////////////////
+#include "squtils.h"
+
+struct SQFuncState
+{
+       SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed);
+       ~SQFuncState();
+#ifdef _DEBUG_DUMP
+       void Dump(SQFunctionProto *func);
+#endif
+       void Error(const SQChar *err);
+       SQFuncState *PushChildState(SQSharedState *ss);
+       void PopChildState();
+       void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);}
+       void AddInstruction(SQInstruction &i);
+       void SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0);
+       void SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val);
+       SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];}
+       void PopInstructions(SQInteger size){for(SQInteger i=0;i<size;i++)_instructions.pop_back();}
+       void SetStackSize(SQInteger n);
+       void SnoozeOpt(){_optimization=false;}
+       SQInteger GetCurrentPos(){return _instructions.size()-1;}
+       SQInteger GetNumericConstant(const SQInteger cons);
+       SQInteger GetNumericConstant(const SQFloat cons);
+       SQInteger PushLocalVariable(const SQObject &name);
+       void AddParameter(const SQObject &name);
+       void AddOuterValue(const SQObject &name);
+       SQInteger GetLocalVariable(const SQObject &name);
+       SQInteger GetOuterVariable(const SQObject &name);
+       SQInteger GenerateCode();
+       SQInteger GetStackSize();
+       SQInteger CalcStackFrameSize();
+       void AddLineInfos(SQInteger line,bool lineop,bool force=false);
+       SQFunctionProto *BuildProto();
+       SQInteger AllocStackPos();
+       SQInteger PushTarget(SQInteger n=-1);
+       SQInteger PopTarget();
+       SQInteger TopTarget();
+       SQInteger GetUpTarget(SQInteger n);
+       bool IsLocal(SQUnsignedInteger stkpos);
+       SQObject CreateString(const SQChar *s,SQInteger len = -1);
+       SQInteger _returnexp;
+       SQLocalVarInfoVec _vlocals;
+       SQIntVec _targetstack;
+       SQInteger _stacksize;
+       bool _varparams;
+       bool _bgenerator;
+       SQIntVec _unresolvedbreaks;
+       SQIntVec _unresolvedcontinues;
+       SQObjectPtrVec _functions;
+       SQObjectPtrVec _parameters;
+       SQOuterVarVec _outervalues;
+       SQInstructionVec _instructions;
+       SQLocalVarInfoVec _localvarinfos;
+       SQObjectPtr _literals;
+       SQObjectPtr _strings;
+       SQObjectPtr _name;
+       SQObjectPtr _sourcename;
+       SQInteger _nliterals;
+       SQLineInfoVec _lineinfos;
+       SQFuncState *_parent;
+       SQIntVec _breaktargets;
+       SQIntVec _continuetargets;
+       SQInteger _lastline;
+       SQInteger _traps; //contains number of nested exception traps
+       bool _optimization;
+       SQSharedState *_sharedstate;
+       sqvector<SQFuncState*> _childstates;
+       SQInteger GetConstant(const SQObject &cons);
+private:
+       CompilerErrorFunc _errfunc;
+       void *_errtarget;
+};
+
+
+#endif //_SQFUNCSTATE_H_
+
diff --git a/src/squirrel/squirrel/sqlexer.cpp b/src/squirrel/squirrel/sqlexer.cpp
new file mode 100644 (file)
index 0000000..701dc79
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include "sqtable.h"
+#include "sqstring.h"
+#include "sqcompiler.h"
+#include "sqlexer.h"
+
+#define CUR_CHAR (_currdata)
+#define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;}
+#define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB)
+#define NEXT() {Next();_currentcolumn++;}
+#define INIT_TEMP_STRING() { _longstr.resize(0);}
+#define APPEND_CHAR(c) { _longstr.push_back(c);}
+#define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));}
+#define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id))
+
+SQLexer::SQLexer(){}
+SQLexer::~SQLexer()
+{
+       _keywords->Release();
+}
+
+void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed)
+{
+       _errfunc = efunc;
+       _errtarget = ed;
+       _sharedstate = ss;
+       _keywords = SQTable::Create(ss, 26);
+       ADD_KEYWORD(while, TK_WHILE);
+       ADD_KEYWORD(do, TK_DO);
+       ADD_KEYWORD(if, TK_IF);
+       ADD_KEYWORD(else, TK_ELSE);
+       ADD_KEYWORD(break, TK_BREAK);
+       ADD_KEYWORD(continue, TK_CONTINUE);
+       ADD_KEYWORD(return, TK_RETURN);
+       ADD_KEYWORD(null, TK_NULL);
+       ADD_KEYWORD(function, TK_FUNCTION);
+       ADD_KEYWORD(local, TK_LOCAL);
+       ADD_KEYWORD(for, TK_FOR);
+       ADD_KEYWORD(foreach, TK_FOREACH);
+       ADD_KEYWORD(in, TK_IN);
+       ADD_KEYWORD(typeof, TK_TYPEOF);
+       ADD_KEYWORD(delegate, TK_DELEGATE);
+       ADD_KEYWORD(delete, TK_DELETE);
+       ADD_KEYWORD(try, TK_TRY);
+       ADD_KEYWORD(catch, TK_CATCH);
+       ADD_KEYWORD(throw, TK_THROW);
+       ADD_KEYWORD(clone, TK_CLONE);
+       ADD_KEYWORD(yield, TK_YIELD);
+       ADD_KEYWORD(resume, TK_RESUME);
+       ADD_KEYWORD(switch, TK_SWITCH);
+       ADD_KEYWORD(case, TK_CASE);
+       ADD_KEYWORD(default, TK_DEFAULT);
+       ADD_KEYWORD(this, TK_THIS);
+       ADD_KEYWORD(parent,TK_PARENT);
+       ADD_KEYWORD(class,TK_CLASS);
+       ADD_KEYWORD(extends,TK_EXTENDS);
+       ADD_KEYWORD(constructor,TK_CONSTRUCTOR);
+       ADD_KEYWORD(instanceof,TK_INSTANCEOF);
+       ADD_KEYWORD(vargc,TK_VARGC);
+       ADD_KEYWORD(vargv,TK_VARGV);
+       ADD_KEYWORD(true,TK_TRUE);
+       ADD_KEYWORD(false,TK_FALSE);
+       ADD_KEYWORD(static,TK_STATIC);
+
+       _readf = rg;
+       _up = up;
+       _lasttokenline = _currentline = 1;
+       _currentcolumn = 0;
+       _prevtoken = -1;
+       Next();
+}
+
+void SQLexer::Error(const SQChar *err)
+{
+       _errfunc(_errtarget,err);
+}
+
+void SQLexer::Next()
+{
+       SQInteger t = _readf(_up);
+       if(t > MAX_CHAR) Error(_SC("Invalid character"));
+       if(t != 0) {
+               _currdata = (LexChar)t;
+               return;
+       }
+       _currdata = SQUIRREL_EOB;
+}
+
+const SQChar *SQLexer::Tok2Str(SQInteger tok)
+{
+       SQObjectPtr itr, key, val;
+       SQInteger nitr;
+       while((nitr = _keywords->Next(false,itr, key, val)) != -1) {
+               itr = (SQInteger)nitr;
+               if(((SQInteger)_integer(val)) == tok)
+                       return _stringval(key);
+       }
+       return NULL;
+}
+
+void SQLexer::LexBlockComment()
+{
+       bool done = false;
+       while(!done) {
+               switch(CUR_CHAR) {
+                       case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue;
+                       case _SC('\n'): _currentline++; NEXT(); continue;
+                       case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment"));
+                       default: NEXT();
+               }
+       }
+}
+
+SQInteger SQLexer::Lex()
+{
+       _lasttokenline = _currentline;
+       while(CUR_CHAR != SQUIRREL_EOB) {
+               switch(CUR_CHAR){
+               case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue;
+               case _SC('\n'):
+                       _currentline++;
+                       _prevtoken=_curtoken;
+                       _curtoken=_SC('\n');
+                       NEXT();
+                       _currentcolumn=1;
+                       continue;
+               case _SC('/'):
+                       NEXT();
+                       switch(CUR_CHAR){
+                       case _SC('*'):
+                               NEXT();
+                               LexBlockComment();
+                               continue;       
+                       case _SC('/'):
+                               do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB()));
+                               continue;
+                       case _SC('='):
+                               NEXT();
+                               RETURN_TOKEN(TK_DIVEQ);
+                               continue;
+                       case _SC('>'):
+                               NEXT();
+                               RETURN_TOKEN(TK_ATTR_CLOSE);
+                               continue;
+                       default:
+                               RETURN_TOKEN('/');
+                       }
+               case _SC('='):
+                       NEXT();
+                       if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') }
+                       else { NEXT(); RETURN_TOKEN(TK_EQ); }
+               case _SC('<'):
+                       NEXT();
+                       if ( CUR_CHAR == _SC('=') ) { NEXT(); RETURN_TOKEN(TK_LE) }
+                       else if ( CUR_CHAR == _SC('-') ) { NEXT(); RETURN_TOKEN(TK_NEWSLOT); }
+                       else if ( CUR_CHAR == _SC('<') ) { NEXT(); RETURN_TOKEN(TK_SHIFTL); }
+                       else if ( CUR_CHAR == _SC('/') ) { NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); }
+                       //else if ( CUR_CHAR == _SC('[') ) { NEXT(); ReadMultilineString(); RETURN_TOKEN(TK_STRING_LITERAL); }
+                       else { RETURN_TOKEN('<') }
+               case _SC('>'):
+                       NEXT();
+                       if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);}
+                       else if(CUR_CHAR == _SC('>')){ 
+                               NEXT(); 
+                               if(CUR_CHAR == _SC('>')){
+                                       NEXT();
+                                       RETURN_TOKEN(TK_USHIFTR);
+                               }
+                               RETURN_TOKEN(TK_SHIFTR);
+                       }
+                       else { RETURN_TOKEN('>') }
+               case _SC('!'):
+                       NEXT();
+                       if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')}
+                       else { NEXT(); RETURN_TOKEN(TK_NE); }
+               case _SC('@'): {
+                       SQInteger stype;
+                       NEXT(); 
+                       if(CUR_CHAR != _SC('"'))
+                               Error(_SC("string expected"));
+                       if((stype=ReadString('"',true))!=-1) {
+                               RETURN_TOKEN(stype);
+                       }
+                       Error(_SC("error parsing the string"));
+                                          }
+               case _SC('"'):
+               case _SC('\''): {
+                       SQInteger stype;
+                       if((stype=ReadString(CUR_CHAR,false))!=-1){
+                               RETURN_TOKEN(stype);
+                       }
+                       Error(_SC("error parsing the string"));
+                       }
+               case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'):
+               case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'):
+                       {SQInteger ret = CUR_CHAR;
+                       NEXT(); RETURN_TOKEN(ret); }
+               case _SC('.'):
+                       NEXT();
+                       if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') }
+                       NEXT();
+                       if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); }
+                       NEXT();
+                       RETURN_TOKEN(TK_VARPARAMS);
+               case _SC('&'):
+                       NEXT();
+                       if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') }
+                       else { NEXT(); RETURN_TOKEN(TK_AND); }
+               case _SC('|'):
+                       NEXT();
+                       if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') }
+                       else { NEXT(); RETURN_TOKEN(TK_OR); }
+               case _SC(':'):
+                       NEXT();
+                       if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') }
+                       else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); }
+               case _SC('*'):
+                       NEXT();
+                       if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);}
+                       else RETURN_TOKEN('*');
+               case _SC('%'):
+                       NEXT();
+                       if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);}
+                       else RETURN_TOKEN('%');
+               case _SC('-'):
+                       NEXT();
+                       if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);}
+                       else if  (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);}
+                       else RETURN_TOKEN('-');
+               case _SC('+'):
+                       NEXT();
+                       if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);}
+                       else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);}
+                       else RETURN_TOKEN('+');
+               case SQUIRREL_EOB:
+                       return 0;
+               default:{
+                               if (scisdigit(CUR_CHAR)) {
+                                       SQInteger ret = ReadNumber();
+                                       RETURN_TOKEN(ret);
+                               }
+                               else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) {
+                                       SQInteger t = ReadID();
+                                       RETURN_TOKEN(t);
+                               }
+                               else {
+                                       SQInteger c = CUR_CHAR;
+                                       if (sciscntrl((int)c)) Error(_SC("unexpected character(control)"));
+                                       NEXT();
+                                       RETURN_TOKEN(c);  
+                               }
+                               RETURN_TOKEN(0);
+                       }
+               }
+       }
+       return 0;    
+}
+       
+SQInteger SQLexer::GetIDType(SQChar *s)
+{
+       SQObjectPtr t;
+       if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {
+               return SQInteger(_integer(t));
+       }
+       return TK_IDENTIFIER;
+}
+
+
+SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim)
+{
+       INIT_TEMP_STRING();
+       NEXT();
+       if(IS_EOB()) return -1;
+       for(;;) {
+               while(CUR_CHAR != ndelim) {
+                       switch(CUR_CHAR) {
+                       case SQUIRREL_EOB:
+                               Error(_SC("unfinished string"));
+                               return -1;
+                       case _SC('\n'): 
+                               if(!verbatim) Error(_SC("newline in a constant")); 
+                               APPEND_CHAR(CUR_CHAR); NEXT(); 
+                               _currentline++;
+                               break;
+                       case _SC('\\'):
+                               if(verbatim) {
+                                       APPEND_CHAR('\\'); NEXT(); 
+                               }
+                               else {
+                                       NEXT();
+                                       switch(CUR_CHAR) {
+                                       case _SC('x'): NEXT(); {
+                                               if(!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); 
+                                               const SQInteger maxdigits = 4;
+                                               SQChar temp[maxdigits+1];
+                                               SQInteger n = 0;
+                                               while(isxdigit(CUR_CHAR) && n < maxdigits) {
+                                                       temp[n] = CUR_CHAR;
+                                                       n++;
+                                                       NEXT();
+                                               }
+                                               temp[n] = 0;
+                                               SQChar *sTemp;
+                                               APPEND_CHAR((SQChar)scstrtoul(temp,&sTemp,16));
+                                       }
+                                   break;
+                                       case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break;
+                                       case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break;
+                                       case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break;
+                                       case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break;
+                                       case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break;
+                                       case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break;
+                                       case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break;
+                                       case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break;
+                                       case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break;
+                                       case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break;
+                                       case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break;
+                                       default:
+                                               Error(_SC("unrecognised escaper char"));
+                                       break;
+                                       }
+                               }
+                               break;
+                       default:
+                               APPEND_CHAR(CUR_CHAR);
+                               NEXT();
+                       }
+               }
+               NEXT();
+               if(verbatim && CUR_CHAR == '"') { //double quotation
+                       APPEND_CHAR(CUR_CHAR);
+                       NEXT();
+               }
+               else {
+                       break;
+               }
+       }
+       TERMINATE_BUFFER();
+       SQInteger len = _longstr.size()-1;
+       if(ndelim == _SC('\'')) {
+               if(len == 0) Error(_SC("empty constant"));
+               if(len > 1) Error(_SC("constant too long"));
+               _nvalue = _longstr[0];
+               return TK_INTEGER;
+       }
+       _svalue = &_longstr[0];
+       return TK_STRING_LITERAL;
+}
+
+void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res)
+{
+       *res = 0;
+       while(*s != 0)
+       {
+               if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0');
+               else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10);
+               else { assert(0); }
+       }
+}
+
+void LexInteger(const SQChar *s,SQUnsignedInteger *res)
+{
+       *res = 0;
+       while(*s != 0)
+       {
+               *res = (*res)*10+((*s++)-'0');
+       }
+}
+
+SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }
+#define MAX_HEX_DIGITS (sizeof(SQInteger)*2)
+SQInteger SQLexer::ReadNumber()
+{
+#define TINT 1
+#define TFLOAT 2
+#define THEX 3
+#define TSCIENTIFIC 4
+       SQInteger type = TINT, firstchar = CUR_CHAR;
+       SQChar *sTemp;
+       INIT_TEMP_STRING();
+       NEXT();
+       if(firstchar == _SC('0') && toupper(CUR_CHAR) == _SC('X')) {
+               NEXT();
+               type = THEX;
+               while(isxdigit(CUR_CHAR)) {
+                       APPEND_CHAR(CUR_CHAR);
+                       NEXT();
+               }
+               if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number"));
+       }
+       else {
+               APPEND_CHAR((int)firstchar);
+               while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {
+            if(CUR_CHAR == _SC('.')) type = TFLOAT;
+                       if(isexponent(CUR_CHAR)) {
+                               if(type != TFLOAT) Error(_SC("invalid numeric format"));
+                               type = TSCIENTIFIC;
+                               APPEND_CHAR(CUR_CHAR);
+                               NEXT();
+                               if(CUR_CHAR == '+' || CUR_CHAR == '-'){
+                                       APPEND_CHAR(CUR_CHAR);
+                                       NEXT();
+                               }
+                               if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected"));
+                       }
+                       
+                       APPEND_CHAR(CUR_CHAR);
+                       NEXT();
+               }
+       }
+       TERMINATE_BUFFER();
+       switch(type) {
+       case TSCIENTIFIC:
+       case TFLOAT:
+               _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp);
+               return TK_FLOAT;
+       case TINT:
+               LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
+               return TK_INTEGER;
+       case THEX:
+               LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
+               return TK_INTEGER;
+       }
+       return 0;
+}
+
+SQInteger SQLexer::ReadID()
+{
+       SQInteger res;
+       INIT_TEMP_STRING();
+       do {
+               APPEND_CHAR(CUR_CHAR);
+               NEXT();
+       } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_'));
+       TERMINATE_BUFFER();
+       res = GetIDType(&_longstr[0]);
+       if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {
+               _svalue = &_longstr[0];
+       }
+       return res;
+}
diff --git a/src/squirrel/squirrel/sqlexer.h b/src/squirrel/squirrel/sqlexer.h
new file mode 100644 (file)
index 0000000..52993e5
--- /dev/null
@@ -0,0 +1,45 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQLEXER_H_
+#define _SQLEXER_H_
+
+#ifdef _UNICODE
+typedef SQChar LexChar;
+#else
+typedef        unsigned char LexChar;
+#endif
+
+struct SQLexer
+{
+       SQLexer();
+       ~SQLexer();
+       void Init(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed);
+       void Error(const SQChar *err);
+       SQInteger Lex();
+       const SQChar *Tok2Str(SQInteger tok);
+private:
+       SQInteger GetIDType(SQChar *s);
+       SQInteger ReadString(SQInteger ndelim,bool verbatim);
+       SQInteger ReadNumber();
+       void LexBlockComment();
+       SQInteger ReadID();
+       void Next();
+       SQInteger _curtoken;
+       SQTable *_keywords;
+public:
+       SQInteger _prevtoken;
+       SQInteger _currentline;
+       SQInteger _lasttokenline;
+       SQInteger _currentcolumn;
+       const SQChar *_svalue;
+       SQInteger _nvalue;
+       SQFloat _fvalue;
+       SQLEXREADFUNC _readf;
+       SQUserPointer _up;
+       LexChar _currdata;
+       SQSharedState *_sharedstate;
+       sqvector<SQChar> _longstr;
+       CompilerErrorFunc _errfunc;
+       void *_errtarget;
+};
+
+#endif
diff --git a/src/squirrel/squirrel/sqmem.cpp b/src/squirrel/squirrel/sqmem.cpp
new file mode 100644 (file)
index 0000000..6faf816
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+void *sq_vm_malloc(SQUnsignedInteger size){    return malloc(size); }
+
+void *sq_vm_realloc(void *p, SQUnsignedInteger oldsize, SQUnsignedInteger size){ return realloc(p, size); }
+
+void sq_vm_free(void *p, SQUnsignedInteger size){      free(p); }
diff --git a/src/squirrel/squirrel/sqobject.cpp b/src/squirrel/squirrel/sqobject.cpp
new file mode 100644 (file)
index 0000000..7bfc19d
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqarray.h"
+#include "sqtable.h"
+#include "squserdata.h"
+#include "sqfuncproto.h"
+#include "sqclass.h"
+#include "sqclosure.h"
+
+SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len)
+{
+       SQString *str=ADD_STRING(ss,s,len);
+       str->_sharedstate=ss;
+       return str;
+}
+
+void SQString::Release()
+{
+       REMOVE_STRING(_sharedstate,this);
+}
+
+SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+       SQInteger idx = (SQInteger)TranslateIndex(refpos);
+       while(idx < _len){
+               outkey = (SQInteger)idx;
+               outval = SQInteger(_val[idx]);
+               //return idx for the next iteration
+               return ++idx;
+       }
+       //nothing to iterate anymore
+       return -1;
+}
+
+SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx)
+{
+       switch(type(idx)){
+               case OT_NULL:
+                       return 0;
+               case OT_INTEGER:
+                       return (SQUnsignedInteger)_integer(idx);
+               default: assert(0); break;
+       }
+       return 0;
+}
+
+SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type)
+{
+       if(!_weakref) {
+               sq_new(_weakref,SQWeakRef);
+               _weakref->_obj._type = type;
+               _weakref->_obj._unVal.pRefCounted = this;
+       }
+       return _weakref;
+}
+
+SQRefCounted::~SQRefCounted()
+{
+       if(_weakref) {
+               _weakref->_obj._type = OT_NULL;
+               _weakref->_obj._unVal.pRefCounted = NULL;
+       }
+}
+
+void SQWeakRef::Release() { 
+       if(ISREFCOUNTED(_obj._type)) { 
+               _obj._unVal.pRefCounted->_weakref = NULL;
+       } 
+       sq_delete(this,SQWeakRef);
+}
+
+bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) {
+       if(_delegate) {
+               return _delegate->Get((*_ss(v)->_metamethods)[mm],res);
+       }
+       return false;
+}
+
+bool SQDelegable::SetDelegate(SQTable *mt)
+{
+       SQTable *temp = mt;
+       while (temp) {
+               if (temp->_delegate == this) return false; //cycle detected
+               temp = temp->_delegate;
+       }
+       if (mt) __ObjAddRef(mt);
+       __ObjRelease(_delegate);
+       _delegate = mt;
+       return true;
+}
+
+bool SQGenerator::Yield(SQVM *v)
+{
+       if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator"));  return false;}
+       if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; }
+       SQInteger size = v->_top-v->_stackbase;
+       _ci=*v->ci;
+       _stack.resize(size);
+       for(SQInteger n =0; n<size; n++) {
+               _stack._vals[n] = v->_stack[v->_stackbase+n];
+               v->_stack[v->_stackbase+n] = _null_;
+       }
+       SQInteger nvargs = v->ci->_vargs.size;
+       SQInteger vargsbase = v->ci->_vargs.base;
+       for(SQInteger j = nvargs - 1; j >= 0; j--) {
+               _vargsstack.push_back(v->_vargsstack[vargsbase+j]);
+       }
+       _ci._generator=_null_;
+       for(SQInteger i=0;i<_ci._etraps;i++) {
+               _etraps.push_back(v->_etraps.top());
+               v->_etraps.pop_back();
+       }
+       _state=eSuspended;
+       return true;
+}
+
+bool SQGenerator::Resume(SQVM *v,SQInteger target)
+{
+       SQInteger size=_stack.size();
+       if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; }
+       if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; }
+       SQInteger prevtop=v->_top-v->_stackbase;
+       PUSH_CALLINFO(v,_ci);
+       SQInteger oldstackbase=v->_stackbase;
+       v->_stackbase = v->_top;
+       v->ci->_target = (SQInt32)target;
+       v->ci->_generator = SQObjectPtr(this);
+       v->ci->_vargs.size = (unsigned short)_vargsstack.size();
+       
+       for(SQInteger i=0;i<_ci._etraps;i++) {
+               v->_etraps.push_back(_etraps.top());
+               _etraps.pop_back();
+       }
+       for(SQInteger n =0; n<size; n++) {
+               v->_stack[v->_stackbase+n] = _stack._vals[n];
+               _stack._vals[0] = _null_;
+       }
+       while(_vargsstack.size()) {
+               v->_vargsstack.push_back(_vargsstack.back());
+               _vargsstack.pop_back();
+       }
+       v->ci->_vargs.base = (unsigned short)(v->_vargsstack.size() - v->ci->_vargs.size);
+       v->_top=v->_stackbase+size;
+       v->ci->_prevtop = (SQInt32)prevtop;
+       v->ci->_prevstkbase = (SQInt32)(v->_stackbase - oldstackbase);
+       _state=eRunning;
+       return true;
+}
+
+void SQArray::Extend(const SQArray *a){
+       SQInteger xlen;
+       if((xlen=a->Size()))
+               for(SQInteger i=0;i<xlen;i++)
+                       Append(a->_values[i]);
+}
+
+const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop)
+{
+       SQUnsignedInteger nvars=_nlocalvarinfos;
+       const SQChar *res=NULL; 
+       if(nvars>=nseq){
+               for(SQUnsignedInteger i=0;i<nvars;i++){
+                       if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop)
+                       {
+                               if(nseq==0){
+                                       vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);
+                                       res=_stringval(_localvarinfos[i]._name);
+                                       break;
+                               }
+                               nseq--;
+                       }
+               }
+       }
+       return res;
+}
+
+SQInteger SQFunctionProto::GetLine(SQInstruction *curr)
+{
+       SQInteger op = (SQInteger)(curr-_instructions);
+       SQInteger line=_lineinfos[0]._line;
+       for(SQInteger i=1;i<_nlineinfos;i++){
+               if(_lineinfos[i]._op>=op)
+                       return line;
+               line=_lineinfos[i]._line;
+       }
+       return line;
+}
+
+#define _CHECK_IO(exp)  { if(!exp)return false; }
+bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size)
+{
+       if(write(up,dest,size) != size) {
+               v->Raise_Error(_SC("io error (write function failure)"));
+               return false;
+       }
+       return true;
+}
+
+bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size)
+{
+       if(size && read(up,dest,size) != size) {
+               v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated"));
+               return false;
+       }
+       return true;
+}
+
+bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQInteger tag)
+{
+       return SafeWrite(v,write,up,&tag,sizeof(tag));
+}
+
+bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQInteger tag)
+{
+       SQInteger t;
+       _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t)));
+       if(t != tag){
+               v->Raise_Error(_SC("invalid or corrupted closure stream"));
+               return false;
+       }
+       return true;
+}
+
+bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o)
+{
+       _CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType)));
+       switch(type(o)){
+       case OT_STRING:
+               _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger)));
+               _CHECK_IO(SafeWrite(v,write,up,_stringval(o),rsl(_string(o)->_len)));
+               break;
+       case OT_INTEGER:
+               _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;
+       case OT_FLOAT:
+               _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break;
+       case OT_NULL:
+               break;
+       default:
+               v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o));
+               return false;
+       }
+       return true;
+}
+
+bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
+{
+       SQObjectType t;
+       _CHECK_IO(SafeRead(v,read,up,&t,sizeof(SQObjectType)));
+       switch(t){
+       case OT_STRING:{
+               SQInteger len;
+               _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger)));
+               _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(rsl(len)),rsl(len)));
+               o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len);
+                                  }
+               break;
+       case OT_INTEGER:{
+               SQInteger i;
+               _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break;
+                                       }
+       case OT_FLOAT:{
+               SQFloat f;
+               _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break;
+                                 }
+       case OT_NULL:
+               o=_null_;
+               break;
+       default:
+               v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t));
+               return false;
+       }
+       return true;
+}
+
+bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
+{
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD));
+       _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar)));
+       _CHECK_IO(_funcproto(_function)->Save(v,up,write));
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));
+       return true;
+}
+
+bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
+{
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD));
+       _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar)));
+       SQObjectPtr func;
+       _CHECK_IO(SQFunctionProto::Load(v,up,read,func));
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));
+       ret = SQClosure::Create(_ss(v),_funcproto(func));
+       return true;
+}
+
+bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
+{
+       SQInteger i,nliterals = _nliterals,nparameters = _nparameters;
+       SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos;
+       SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions;
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(WriteObject(v,up,write,_sourcename));
+       _CHECK_IO(WriteObject(v,up,write,_name));
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals)));
+       _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters)));
+       _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues)));
+       _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos)));
+       _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos)));
+       _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions)));
+       _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions)));
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       for(i=0;i<nliterals;i++){
+               _CHECK_IO(WriteObject(v,up,write,_literals[i]));
+       }
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       for(i=0;i<nparameters;i++){
+               _CHECK_IO(WriteObject(v,up,write,_parameters[i]));
+       }
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       for(i=0;i<noutervalues;i++){
+               _CHECK_IO(SafeWrite(v,write,up,&_outervalues[i]._type,sizeof(SQUnsignedInteger)));
+               _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._src));
+               _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._name));
+       }
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       for(i=0;i<nlocalvarinfos;i++){
+               SQLocalVarInfo &lvi=_localvarinfos[i];
+               _CHECK_IO(WriteObject(v,up,write,lvi._name));
+               _CHECK_IO(SafeWrite(v,write,up,&lvi._pos,sizeof(SQUnsignedInteger)));
+               _CHECK_IO(SafeWrite(v,write,up,&lvi._start_op,sizeof(SQUnsignedInteger)));
+               _CHECK_IO(SafeWrite(v,write,up,&lvi._end_op,sizeof(SQUnsignedInteger)));
+       }
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeWrite(v,write,up,_lineinfos,sizeof(SQLineInfo)*nlineinfos));
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeWrite(v,write,up,_instructions,sizeof(SQInstruction)*ninstructions));
+
+       _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+       for(i=0;i<nfunctions;i++){
+               _CHECK_IO(_funcproto(_functions[i])->Save(v,up,write));
+       }
+       _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize)));
+       _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator)));
+       _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams)));
+       return true;
+}
+
+bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
+{
+       SQInteger i, nliterals,nparameters;
+       SQInteger noutervalues ,nlocalvarinfos ;
+       SQInteger nlineinfos,ninstructions ,nfunctions ;
+       SQObjectPtr sourcename, name;
+       SQObjectPtr o;
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(ReadObject(v, up, read, sourcename));
+       _CHECK_IO(ReadObject(v, up, read, name));
+       
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals)));
+       _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters)));
+       _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues)));
+       _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos)));
+       _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos)));
+       _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions)));
+       _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions)));
+
+       SQFunctionProto *f = SQFunctionProto::Create(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos);
+       SQObjectPtr proto = f; //gets a ref in case of failure
+       f->_sourcename = sourcename;
+       f->_name = name;
+
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+       for(i = 0;i < nliterals; i++){
+               _CHECK_IO(ReadObject(v, up, read, o));
+               f->_literals[i] = o;
+       }
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+       for(i = 0; i < nparameters; i++){
+               _CHECK_IO(ReadObject(v, up, read, o));
+               f->_parameters[i] = o;
+       }
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+       for(i = 0; i < noutervalues; i++){
+               SQUnsignedInteger type;
+               SQObjectPtr name;
+               _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger)));
+               _CHECK_IO(ReadObject(v, up, read, o));
+               _CHECK_IO(ReadObject(v, up, read, name));
+               f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type);
+       }
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+       for(i = 0; i < nlocalvarinfos; i++){
+               SQLocalVarInfo lvi;
+               _CHECK_IO(ReadObject(v, up, read, lvi._name));
+               _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger)));
+               _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger)));
+               _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger)));
+               f->_localvarinfos[i] = lvi;
+       }
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos));
+
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+       _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions));
+
+       _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+       for(i = 0; i < nfunctions; i++){
+               _CHECK_IO(_funcproto(o)->Load(v, up, read, o));
+               f->_functions[i] = o;
+       }
+       _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize)));
+       _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator)));
+       _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams)));
+       ret = f;
+       return true;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+
+#define START_MARK()   if(!(_uiRef&MARK_FLAG)){ \
+               _uiRef|=MARK_FLAG;
+
+#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \
+               AddToChain(chain, this); }
+
+void SQVM::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               SQSharedState::MarkObject(_lasterror,chain);
+               SQSharedState::MarkObject(_errorhandler,chain);
+               SQSharedState::MarkObject(_debughook,chain);
+               SQSharedState::MarkObject(_roottable, chain);
+               SQSharedState::MarkObject(temp_reg, chain);
+               for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
+               for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
+       END_MARK()
+}
+
+void SQArray::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               SQInteger len = _values.size();
+               for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain);
+       END_MARK()
+}
+void SQTable::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               if(_delegate) _delegate->Mark(chain);
+               SQInteger len = _numofnodes;
+               for(SQInteger i = 0; i < len; i++){
+                       SQSharedState::MarkObject(_nodes[i].key, chain);
+                       SQSharedState::MarkObject(_nodes[i].val, chain);
+               }
+       END_MARK()
+}
+
+void SQClass::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               _members->Mark(chain);
+               if(_base) _base->Mark(chain);
+               SQSharedState::MarkObject(_attributes, chain);
+               for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) {
+                       SQSharedState::MarkObject(_defaultvalues[i].val, chain);
+                       SQSharedState::MarkObject(_defaultvalues[i].attrs, chain);
+               }
+               for(SQUnsignedInteger j =0; j< _methods.size(); j++) {
+                       SQSharedState::MarkObject(_methods[j].val, chain);
+                       SQSharedState::MarkObject(_methods[j].attrs, chain);
+               }
+               for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) {
+                       SQSharedState::MarkObject(_metamethods[k], chain);
+               }
+       END_MARK()
+}
+
+void SQInstance::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               _class->Mark(chain);
+               SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+               for(SQUnsignedInteger i =0; i< nvalues; i++) {
+                       SQSharedState::MarkObject(_values[i], chain);
+               }
+       END_MARK()
+}
+
+void SQGenerator::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
+               for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
+               SQSharedState::MarkObject(_closure, chain);
+       END_MARK()
+}
+
+void SQClosure::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
+       END_MARK()
+}
+
+void SQNativeClosure::Mark(SQCollectable **chain)
+{
+       START_MARK()
+               for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
+       END_MARK()
+}
+
+void SQUserData::Mark(SQCollectable **chain){
+       START_MARK()
+               if(_delegate) _delegate->Mark(chain);
+       END_MARK()
+}
+
+void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; }
+
+#endif
+
diff --git a/src/squirrel/squirrel/sqobject.h b/src/squirrel/squirrel/sqobject.h
new file mode 100644 (file)
index 0000000..02d5967
--- /dev/null
@@ -0,0 +1,349 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQOBJECT_H_
+#define _SQOBJECT_H_
+
+#include "squtils.h"
+
+#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R'))
+#define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T'))
+#define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L'))
+
+struct SQSharedState;
+
+enum SQMetaMethod{
+       MT_ADD=0,
+       MT_SUB=1,
+       MT_MUL=2,
+       MT_DIV=3,
+       MT_UNM=4,
+       MT_MODULO=5,
+       MT_SET=6,
+       MT_GET=7,
+       MT_TYPEOF=8,
+       MT_NEXTI=9,
+       MT_CMP=10,
+       MT_CALL=11,
+       MT_CLONED=12,
+       MT_NEWSLOT=13,
+       MT_DELSLOT=14,
+       MT_TOSTRING=15,
+       MT_NEWMEMBER=16,
+       MT_INHERITED=17,
+       MT_LAST = 18
+};
+
+#define MM_ADD         _SC("_add")
+#define MM_SUB         _SC("_sub")
+#define MM_MUL         _SC("_mul")
+#define MM_DIV         _SC("_div")
+#define MM_UNM         _SC("_unm")
+#define MM_MODULO      _SC("_modulo")
+#define MM_SET         _SC("_set")
+#define MM_GET         _SC("_get")
+#define MM_TYPEOF      _SC("_typeof")
+#define MM_NEXTI       _SC("_nexti")
+#define MM_CMP         _SC("_cmp")
+#define MM_CALL                _SC("_call")
+#define MM_CLONED      _SC("_cloned")
+#define MM_NEWSLOT     _SC("_newslot")
+#define MM_DELSLOT     _SC("_delslot")
+#define MM_TOSTRING    _SC("_tostring")
+#define MM_NEWMEMBER _SC("_newmember")
+#define MM_INHERITED _SC("_inherited")
+
+#define MINPOWER2 4
+
+struct SQRefCounted
+{
+       SQRefCounted() { _uiRef = 0; _weakref = NULL; }
+       virtual ~SQRefCounted();
+       SQWeakRef *GetWeakRef(SQObjectType type);
+       SQUnsignedInteger _uiRef;
+       struct SQWeakRef *_weakref;
+       virtual void Release()=0;
+};
+
+struct SQWeakRef : SQRefCounted
+{
+       void Release();
+       SQObject _obj;
+};
+
+#define _realval(o) (type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj)
+
+struct SQObjectPtr;
+
+#define __AddRef(type,unval) if(ISREFCOUNTED(type))    \
+               { \
+                       unval.pRefCounted->_uiRef++; \
+               }  
+
+#define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)<=0))     \
+               {       \
+                       unval.pRefCounted->Release();   \
+               }
+
+#define __ObjRelease(obj) { \
+       if((obj)) {     \
+               (obj)->_uiRef--; \
+               if((obj)->_uiRef == 0) \
+                       (obj)->Release(); \
+               (obj) = NULL;   \
+       } \
+}
+
+#define __ObjAddRef(obj) { \
+       (obj)->_uiRef++; \
+}
+
+#define type(obj) ((obj)._type)
+#define is_delegable(t) (type(t)&SQOBJECT_DELEGABLE)
+#define raw_type(obj) _RAW_TYPE((obj)._type)
+
+#define _integer(obj) ((obj)._unVal.nInteger)
+#define _float(obj) ((obj)._unVal.fFloat)
+#define _string(obj) ((obj)._unVal.pString)
+#define _table(obj) ((obj)._unVal.pTable)
+#define _array(obj) ((obj)._unVal.pArray)
+#define _closure(obj) ((obj)._unVal.pClosure)
+#define _generator(obj) ((obj)._unVal.pGenerator)
+#define _nativeclosure(obj) ((obj)._unVal.pNativeClosure)
+#define _userdata(obj) ((obj)._unVal.pUserData)
+#define _userpointer(obj) ((obj)._unVal.pUserPointer)
+#define _thread(obj) ((obj)._unVal.pThread)
+#define _funcproto(obj) ((obj)._unVal.pFunctionProto)
+#define _class(obj) ((obj)._unVal.pClass)
+#define _instance(obj) ((obj)._unVal.pInstance)
+#define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable)
+#define _weakref(obj) ((obj)._unVal.pWeakRef)
+#define _refcounted(obj) ((obj)._unVal.pRefCounted)
+#define _rawval(obj) ((obj)._unVal.pRefCounted)
+
+#define _stringval(obj) (obj)._unVal.pString->_val
+#define _userdataval(obj) (obj)._unVal.pUserData->_val
+
+#define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num))
+#define tointeger(num) ((type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num))
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+struct SQObjectPtr : public SQObject
+{
+       SQObjectPtr()
+       {
+               _type=OT_NULL;
+               _unVal.pUserPointer=NULL;
+       }
+       SQObjectPtr(const SQObjectPtr &o)
+       {
+               _type=o._type;
+               _unVal=o._unVal;
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(const SQObject &o)
+       {
+               _type=o._type;
+               _unVal=o._unVal;
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQTable *pTable)
+       {
+               _type=OT_TABLE;
+               _unVal.pTable=pTable;
+               assert(_unVal.pTable);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQClass *pClass)
+       {
+               _type=OT_CLASS;
+               _unVal.pClass=pClass;
+               assert(_unVal.pClass);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQInstance *pInstance)
+       {
+               _type=OT_INSTANCE;
+               _unVal.pInstance=pInstance;
+               assert(_unVal.pInstance);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQArray *pArray)
+       {
+               _type=OT_ARRAY;
+               _unVal.pArray=pArray;
+               assert(_unVal.pArray);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQClosure *pClosure)
+       {
+               _type=OT_CLOSURE;
+               _unVal.pClosure=pClosure;
+               assert(_unVal.pClosure);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQGenerator *pGenerator)
+       {
+               _type=OT_GENERATOR;
+               _unVal.pGenerator=pGenerator;
+               assert(_unVal.pGenerator);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQNativeClosure *pNativeClosure)
+       {
+               _type=OT_NATIVECLOSURE;
+               _unVal.pNativeClosure=pNativeClosure;
+               assert(_unVal.pNativeClosure);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQString *pString)
+       {
+               _type=OT_STRING;
+               _unVal.pString=pString;
+               assert(_unVal.pString);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQUserData *pUserData)
+       {
+               _type=OT_USERDATA;
+               _unVal.pUserData=pUserData;
+               assert(_unVal.pUserData);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQVM *pThread)
+       {
+               _type=OT_THREAD;
+               _unVal.pThread=pThread;
+               assert(_unVal.pThread);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQWeakRef *pWeakRef)
+       {
+               _type=OT_WEAKREF;
+               _unVal.pWeakRef=pWeakRef;
+               assert(_unVal.pWeakRef);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQFunctionProto *pFunctionProto)
+       {
+               _type=OT_FUNCPROTO;
+               _unVal.pFunctionProto=pFunctionProto;
+               assert(_unVal.pFunctionProto);
+               __AddRef(_type,_unVal);
+       }
+       SQObjectPtr(SQInteger nInteger)
+       {
+               _unVal.pUserPointer=NULL;
+               _type=OT_INTEGER;
+               _unVal.nInteger=nInteger;
+       }
+       SQObjectPtr(SQFloat fFloat)
+       {
+               _unVal.pUserPointer=NULL;
+               _type=OT_FLOAT;
+               _unVal.fFloat=fFloat;
+       }
+       SQObjectPtr(bool bBool)
+       {
+               _unVal.pUserPointer=NULL;
+               _type = OT_BOOL;
+               _unVal.nInteger = bBool?1:0;
+       }
+       SQObjectPtr(SQUserPointer pUserPointer)
+       {
+               _type=OT_USERPOINTER;
+               _unVal.pUserPointer=pUserPointer;
+       }
+       ~SQObjectPtr()
+       {
+               __Release(_type,_unVal);
+       }
+       inline void Null()
+       {
+               SQObjectType tOldType;
+               SQObjectValue unOldVal;
+               tOldType = _type;
+               unOldVal = _unVal;
+               _type = OT_NULL;
+               _unVal.pUserPointer = NULL;
+               __Release(tOldType,unOldVal);
+       }
+       inline SQObjectPtr& operator=(SQInteger i)
+       { 
+               __Release(_type,_unVal);
+               _unVal.nInteger = i;
+               _type = OT_INTEGER;
+               return *this;
+       }
+       inline SQObjectPtr& operator=(SQFloat f)
+       { 
+               __Release(_type,_unVal);
+               _unVal.fFloat = f;
+               _type = OT_FLOAT;
+               return *this;
+       }
+       inline SQObjectPtr& operator=(const SQObjectPtr& obj)
+       { 
+               SQObjectType tOldType;
+               SQObjectValue unOldVal;
+               tOldType=_type;
+               unOldVal=_unVal;
+               _unVal = obj._unVal;
+               _type = obj._type;
+               __AddRef(_type,_unVal);
+               __Release(tOldType,unOldVal);
+               return *this;
+       }
+       inline SQObjectPtr& operator=(const SQObject& obj)
+       { 
+               SQObjectType tOldType;
+               SQObjectValue unOldVal;
+               tOldType=_type;
+               unOldVal=_unVal;
+               _unVal = obj._unVal;
+               _type = obj._type;
+               __AddRef(_type,_unVal);
+               __Release(tOldType,unOldVal);
+               return *this;
+       }
+       private:
+               SQObjectPtr(const SQChar *){} //safety
+};
+/////////////////////////////////////////////////////////////////////////////////////
+#ifndef NO_GARBAGE_COLLECTOR
+#define MARK_FLAG 0x80000000
+struct SQCollectable : public SQRefCounted {
+       SQCollectable *_next;
+       SQCollectable *_prev;
+       SQSharedState *_sharedstate;
+       virtual void Release()=0;
+       virtual void Mark(SQCollectable **chain)=0;
+       void UnMark();
+       virtual void Finalize()=0;
+       static void AddToChain(SQCollectable **chain,SQCollectable *c);
+       static void RemoveFromChain(SQCollectable **chain,SQCollectable *c);
+};
+
+
+#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj)
+#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);}
+#define CHAINABLE_OBJ SQCollectable
+#define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;}
+#else
+
+#define ADD_TO_CHAIN(chain,obj) ((void)0)
+#define REMOVE_FROM_CHAIN(chain,obj) ((void)0)
+#define CHAINABLE_OBJ SQRefCounted
+#define INIT_CHAIN() ((void)0)
+#endif
+
+struct SQDelegable : public CHAINABLE_OBJ {
+       bool SetDelegate(SQTable *m);
+       virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
+       SQTable *_delegate;
+};
+
+SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx);
+typedef sqvector<SQObjectPtr> SQObjectPtrVec;
+typedef sqvector<SQInteger> SQIntVec;
+
+
+#endif //_SQOBJECT_H_
diff --git a/src/squirrel/squirrel/sqopcodes.h b/src/squirrel/squirrel/sqopcodes.h
new file mode 100644 (file)
index 0000000..a05788b
--- /dev/null
@@ -0,0 +1,115 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQOPCODES_H_
+#define _SQOPCODES_H_
+
+#define MAX_FUNC_STACKSIZE 0xFF
+#define MAX_LITERALS ((SQInteger)0x7FFFFFFF)
+
+enum BitWiseOP {
+       BW_AND = 0,
+       BW_OR = 2,      
+       BW_XOR = 3,
+       BW_SHIFTL = 4,
+       BW_SHIFTR = 5,
+       BW_USHIFTR = 6
+};
+
+enum CmpOP {
+       CMP_G = 0,
+       CMP_GE = 2,     
+       CMP_L = 3,
+       CMP_LE = 4
+};
+enum SQOpcode
+{
+       _OP_LINE=                               0x00,   
+       _OP_LOAD=                               0x01,
+       _OP_LOADINT=                    0x02,
+       _OP_LOADFLOAT=                  0x03,
+       _OP_DLOAD=                              0x04,
+       _OP_TAILCALL=                   0x05,   
+       _OP_CALL=                               0x06,   
+       _OP_PREPCALL=                   0x07,   
+       _OP_PREPCALLK=                  0x08,   
+       _OP_GETK=                               0x09,   
+       _OP_MOVE=                               0x0A,   
+       _OP_NEWSLOT=                    0x0B,   
+       _OP_DELETE=                             0x0C,   
+       _OP_SET=                                0x0D,   
+       _OP_GET=                                0x0E,
+       _OP_EQ=                                 0x0F,
+       _OP_NE=                                 0x10,
+       _OP_ARITH=                              0x11,
+       _OP_BITW=                               0x12,
+       _OP_RETURN=                             0x13,   
+       _OP_LOADNULLS=                  0x14,   
+       _OP_LOADROOTTABLE=              0x15,
+       _OP_LOADBOOL=                   0x16,
+       _OP_DMOVE=                              0x17,   
+       _OP_JMP=                                0x18,   
+       _OP_JNZ=                                0x19,   
+       _OP_JZ=                                 0x1A,   
+       _OP_LOADFREEVAR=                0x1B,   
+       _OP_VARGC=                              0x1C,   
+       _OP_GETVARGV=                   0x1D,   
+       _OP_NEWTABLE=                   0x1E,   
+       _OP_NEWARRAY=                   0x1F,   
+       _OP_APPENDARRAY=                0x20,   
+       _OP_GETPARENT=                  0x21,   
+       _OP_COMPARITH=                  0x22,   
+       _OP_COMPARITHL=                 0x23,   
+       _OP_INC=                                0x24,   
+       _OP_INCL=                               0x25,   
+       _OP_PINC=                               0x26,   
+       _OP_PINCL=                              0x27,   
+       _OP_CMP=                                0x28,
+       _OP_EXISTS=                             0x29,   
+       _OP_INSTANCEOF=                 0x2A,
+       _OP_AND=                                0x2B,
+       _OP_OR=                                 0x2C,
+       _OP_NEG=                                0x2D,
+       _OP_NOT=                                0x2E,
+       _OP_BWNOT=                              0x2F,   
+       _OP_CLOSURE=                    0x30,   
+       _OP_YIELD=                              0x31,   
+       _OP_RESUME=                             0x32,
+       _OP_FOREACH=                    0x33,
+       _OP_POSTFOREACH=                0x34,
+       _OP_DELEGATE=                   0x35,
+       _OP_CLONE=                              0x36,
+       _OP_TYPEOF=                             0x37,
+       _OP_PUSHTRAP=                   0x38,
+       _OP_POPTRAP=                    0x39,
+       _OP_THROW=                              0x3A,
+       _OP_CLASS=                              0x3B,
+       _OP_NEWSLOTA=                   0x3C,
+};                                                       
+
+struct SQInstructionDesc {       
+       const SQChar *name;               
+};                                                       
+
+struct SQInstruction 
+{
+       SQInstruction(){};
+       SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0)
+       {       op = _op;
+               _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1;
+               _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3;
+       }
+    
+       
+       SQInt32 _arg1;
+       unsigned char op;
+       unsigned char _arg0;
+       unsigned char _arg2;
+       unsigned char _arg3;
+};
+
+#include "squtils.h"
+typedef sqvector<SQInstruction> SQInstructionVec;
+
+#define NEW_SLOT_ATTRIBUTES_FLAG       0x01
+#define NEW_SLOT_STATIC_FLAG           0x02
+
+#endif // _SQOPCODES_H_
diff --git a/src/squirrel/squirrel/sqpcheader.h b/src/squirrel/squirrel/sqpcheader.h
new file mode 100644 (file)
index 0000000..a3fb037
--- /dev/null
@@ -0,0 +1,19 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQPCHEADER_H_
+#define _SQPCHEADER_H_
+
+#if defined(_MSC_VER) && defined(_DEBUG)
+#include <crtdbg.h>
+#endif 
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <new>
+//squirrel stuff
+#include <squirrel.h>
+#include "sqobject.h"
+#include "sqstate.h"
+
+#endif //_SQPCHEADER_H_
diff --git a/src/squirrel/squirrel/sqstate.cpp b/src/squirrel/squirrel/sqstate.cpp
new file mode 100644 (file)
index 0000000..150bb56
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqopcodes.h"
+#include "sqvm.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "squserdata.h"
+#include "sqclass.h"
+
+SQObjectPtr _null_;
+SQObjectPtr _true_(true);
+SQObjectPtr _false_(false);
+SQObjectPtr _one_((SQInteger)1);
+SQObjectPtr _minusone_((SQInteger)-1);
+
+SQSharedState::SQSharedState()
+{
+       _compilererrorhandler = NULL;
+       _printfunc = NULL;
+       _debuginfo = false;
+       _notifyallexceptions = false;
+}
+
+#define newsysstring(s) {      \
+       _systemstrings->push_back(SQString::Create(this,s));    \
+       }
+
+#define newmetamethod(s) {     \
+       _metamethods->push_back(SQString::Create(this,s));      \
+       _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
+       }
+
+bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
+{
+       SQInteger i = 0;
+       
+       SQInteger mask = 0;
+       while(typemask[i] != 0) {
+               
+               switch(typemask[i]){
+                               case 'o': mask |= _RT_NULL; break;
+                               case 'i': mask |= _RT_INTEGER; break;
+                               case 'f': mask |= _RT_FLOAT; break;
+                               case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;
+                               case 's': mask |= _RT_STRING; break;
+                               case 't': mask |= _RT_TABLE; break;
+                               case 'a': mask |= _RT_ARRAY; break;
+                               case 'u': mask |= _RT_USERDATA; break;
+                               case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;
+                               case 'b': mask |= _RT_BOOL; break;
+                               case 'g': mask |= _RT_GENERATOR; break;
+                               case 'p': mask |= _RT_USERPOINTER; break;
+                               case 'v': mask |= _RT_THREAD; break;
+                               case 'x': mask |= _RT_INSTANCE; break;
+                               case 'y': mask |= _RT_CLASS; break;
+                               case 'r': mask |= _RT_WEAKREF; break;
+                               case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
+                               case ' ': i++; continue; //ignores spaces
+                               default:
+                                       return false;
+               }
+               i++;
+               if(typemask[i] == '|') { 
+                       i++; 
+                       if(typemask[i] == 0)
+                               return false;
+                       continue; 
+               }
+               res.push_back(mask);
+               mask = 0;
+               
+       }
+       return true;
+}
+
+SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)
+{
+       SQInteger i=0;
+       SQTable *t=SQTable::Create(ss,0);
+       while(funcz[i].name!=0){
+               SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
+               nc->_nparamscheck = funcz[i].nparamscheck;
+               nc->_name = SQString::Create(ss,funcz[i].name);
+               if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
+                       return NULL;
+               t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
+               i++;
+       }
+       return t;
+}
+
+void SQSharedState::Init()
+{      
+       _scratchpad=NULL;
+       _scratchpadsize=0;
+#ifndef NO_GARBAGE_COLLECTOR
+       _gc_chain=NULL;
+#endif
+       sq_new(_stringtable,StringTable);
+       sq_new(_metamethods,SQObjectPtrVec);
+       sq_new(_systemstrings,SQObjectPtrVec);
+       sq_new(_types,SQObjectPtrVec);
+       _metamethodsmap = SQTable::Create(this,MT_LAST-1);
+       //adding type strings to avoid memory trashing
+       //types names
+       newsysstring(_SC("null"));
+       newsysstring(_SC("table"));
+       newsysstring(_SC("array"));
+       newsysstring(_SC("closure"));
+       newsysstring(_SC("string"));
+       newsysstring(_SC("userdata"));
+       newsysstring(_SC("integer"));
+       newsysstring(_SC("float"));
+       newsysstring(_SC("userpointer"));
+       newsysstring(_SC("function"));
+       newsysstring(_SC("generator"));
+       newsysstring(_SC("thread"));
+       newsysstring(_SC("class"));
+       newsysstring(_SC("instance"));
+       newsysstring(_SC("bool"));
+       //meta methods
+       newmetamethod(MM_ADD);
+       newmetamethod(MM_SUB);
+       newmetamethod(MM_MUL);
+       newmetamethod(MM_DIV);
+       newmetamethod(MM_UNM);
+       newmetamethod(MM_MODULO);
+       newmetamethod(MM_SET);
+       newmetamethod(MM_GET);
+       newmetamethod(MM_TYPEOF);
+       newmetamethod(MM_NEXTI);
+       newmetamethod(MM_CMP);
+       newmetamethod(MM_CALL);
+       newmetamethod(MM_CLONED);
+       newmetamethod(MM_NEWSLOT);
+       newmetamethod(MM_DELSLOT);
+       newmetamethod(MM_TOSTRING);
+       newmetamethod(MM_NEWMEMBER);
+       newmetamethod(MM_INHERITED);
+
+       _constructoridx = SQString::Create(this,_SC("constructor"));
+       _registry = SQTable::Create(this,0);
+       _table_default_delegate=CreateDefaultDelegate(this,_table_default_delegate_funcz);
+       _array_default_delegate=CreateDefaultDelegate(this,_array_default_delegate_funcz);
+       _string_default_delegate=CreateDefaultDelegate(this,_string_default_delegate_funcz);
+       _number_default_delegate=CreateDefaultDelegate(this,_number_default_delegate_funcz);
+       _closure_default_delegate=CreateDefaultDelegate(this,_closure_default_delegate_funcz);
+       _generator_default_delegate=CreateDefaultDelegate(this,_generator_default_delegate_funcz);
+       _thread_default_delegate=CreateDefaultDelegate(this,_thread_default_delegate_funcz);
+       _class_default_delegate=CreateDefaultDelegate(this,_class_default_delegate_funcz);
+       _instance_default_delegate=CreateDefaultDelegate(this,_instance_default_delegate_funcz);
+       _weakref_default_delegate=CreateDefaultDelegate(this,_weakref_default_delegate_funcz);
+
+}
+
+SQSharedState::~SQSharedState()
+{
+       _constructoridx = _null_;
+       _refs_table.Finalize();
+       _table(_registry)->Finalize();
+       _table(_metamethodsmap)->Finalize();
+       _registry = _null_;
+       _metamethodsmap = _null_;
+       while(!_systemstrings->empty()) {
+               _systemstrings->back()=_null_;
+               _systemstrings->pop_back();
+       }
+       _thread(_root_vm)->Finalize();
+       _root_vm = _null_;
+       _table_default_delegate=_null_;
+       _array_default_delegate=_null_;
+       _string_default_delegate=_null_;
+       _number_default_delegate=_null_;
+       _closure_default_delegate=_null_;
+       _generator_default_delegate=_null_;
+       _thread_default_delegate=_null_;
+       _class_default_delegate=_null_;
+       _instance_default_delegate=_null_;
+       _weakref_default_delegate=_null_;
+       
+#ifndef NO_GARBAGE_COLLECTOR
+       SQCollectable *t=_gc_chain;
+       SQCollectable *nx=NULL;
+       while(t) {
+               t->_uiRef++;
+               t->Finalize();
+               nx=t->_next;
+               if(--t->_uiRef == 0)
+                       t->Release();
+               t=nx;
+       }
+       assert(_gc_chain==NULL); //just to proove a theory
+       while(_gc_chain){
+               _gc_chain->_uiRef++;
+               _gc_chain->Release();
+       }
+#endif
+
+       sq_delete(_types,SQObjectPtrVec);
+       sq_delete(_systemstrings,SQObjectPtrVec);
+       sq_delete(_metamethods,SQObjectPtrVec);
+       sq_delete(_stringtable,StringTable);
+       if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
+}
+
+
+SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
+{
+       if(type(name) != OT_STRING)
+               return -1;
+       SQObjectPtr ret;
+       if(_table(_metamethodsmap)->Get(name,ret)) {
+               return _integer(ret);
+       }
+       return -1;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+
+void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
+{
+       switch(type(o)){
+       case OT_TABLE:_table(o)->Mark(chain);break;
+       case OT_ARRAY:_array(o)->Mark(chain);break;
+       case OT_USERDATA:_userdata(o)->Mark(chain);break;
+       case OT_CLOSURE:_closure(o)->Mark(chain);break;
+       case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
+       case OT_GENERATOR:_generator(o)->Mark(chain);break;
+       case OT_THREAD:_thread(o)->Mark(chain);break;
+       case OT_CLASS:_class(o)->Mark(chain);break;
+       case OT_INSTANCE:_instance(o)->Mark(chain);break;
+       default: break; //shutup compiler
+       }
+}
+
+
+SQInteger SQSharedState::CollectGarbage(SQVM *vm)
+{
+       SQInteger n=0;
+       SQCollectable *tchain=NULL;
+       SQVM *vms = _thread(_root_vm);
+       
+       vms->Mark(&tchain);
+       SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
+       _refs_table.Mark(&tchain);
+       MarkObject(_registry,&tchain);
+       MarkObject(_metamethodsmap,&tchain);
+       MarkObject(_table_default_delegate,&tchain);
+       MarkObject(_array_default_delegate,&tchain);
+       MarkObject(_string_default_delegate,&tchain);
+       MarkObject(_number_default_delegate,&tchain);
+       MarkObject(_generator_default_delegate,&tchain);
+       MarkObject(_thread_default_delegate,&tchain);
+       MarkObject(_closure_default_delegate,&tchain);
+       MarkObject(_class_default_delegate,&tchain);
+       MarkObject(_instance_default_delegate,&tchain);
+       MarkObject(_weakref_default_delegate,&tchain);
+       
+       SQCollectable *t = _gc_chain;
+       SQCollectable *nx = NULL;
+       while(t) {
+               t->_uiRef++;
+               t->Finalize();
+               nx = t->_next;
+               if(--t->_uiRef == 0)
+                       t->Release();
+               t = nx;
+               n++;
+       }
+
+       t = tchain;
+       while(t) {
+               t->UnMark();
+               t = t->_next;
+       }
+       _gc_chain = tchain;
+       SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
+       assert(z == x);
+       return n;
+}
+#endif
+
+#ifndef NO_GARBAGE_COLLECTOR
+void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
+{
+    c->_prev = NULL;
+       c->_next = *chain;
+       if(*chain) (*chain)->_prev = c;
+       *chain = c;
+}
+
+void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
+{
+       if(c->_prev) c->_prev->_next = c->_next;
+       else *chain = c->_next;
+       if(c->_next)
+               c->_next->_prev = c->_prev;
+       c->_next = NULL;
+       c->_prev = NULL;
+}
+#endif
+
+SQChar* SQSharedState::GetScratchPad(SQInteger size)
+{
+       SQInteger newsize;
+       if(size>0) {
+               if(_scratchpadsize < size) {
+                       newsize = size + (size>>1);
+                       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
+                       _scratchpadsize = newsize;
+
+               }else if(_scratchpadsize >= (size<<5)) {
+                       newsize = _scratchpadsize >> 1;
+                       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
+                       _scratchpadsize = newsize;
+               }
+       }
+       return _scratchpad;
+}
+
+RefTable::RefTable()
+{
+       AllocNodes(4);
+}
+
+void RefTable::Finalize()
+{
+       RefNode *nodes = _nodes;
+       for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
+               nodes->obj = _null_;
+               nodes++;
+       }
+}
+
+RefTable::~RefTable()
+{
+       SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+void RefTable::Mark(SQCollectable **chain)
+{
+       RefNode *nodes = (RefNode *)_nodes;
+       for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
+               if(type(nodes->obj) != OT_NULL) {
+                       SQSharedState::MarkObject(nodes->obj,chain);
+               }
+               nodes++;
+       }
+}
+#endif
+
+void RefTable::AddRef(SQObject &obj)
+{
+       SQHash mainpos;
+       RefNode *prev;
+       RefNode *ref = Get(obj,mainpos,&prev,true);
+       ref->refs++;
+}
+
+SQBool RefTable::Release(SQObject &obj)
+{
+       SQHash mainpos;
+       RefNode *prev;
+       RefNode *ref = Get(obj,mainpos,&prev,false);
+       if(ref) {
+               if(--ref->refs == 0) {
+                       SQObjectPtr o = ref->obj;
+                       if(prev) {
+                               prev->next = ref->next;
+                       }
+                       else {
+                               _buckets[mainpos] = ref->next;
+                       }
+                       ref->next = _freelist;
+                       _freelist = ref;
+                       _slotused--;
+                       ref->obj = _null_;
+                       //<<FIXME>>test for shrink?
+                       return SQTrue;
+               }
+       }
+       else {
+               assert(0);
+       }
+       return SQFalse;
+}
+
+void RefTable::Resize(SQUnsignedInteger size)
+{
+       RefNode **oldbucks = _buckets;
+       RefNode *t = _nodes;
+       SQUnsignedInteger oldnumofslots = _numofslots;
+       AllocNodes(size);
+       //rehash
+       SQUnsignedInteger nfound = 0;
+       for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
+               if(type(t->obj) != OT_NULL) {
+                       //add back;
+                       assert(t->refs != 0);
+                       RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);
+                       nn->refs = t->refs; 
+                       t->obj = _null_;
+                       nfound++;
+               }
+               t++;
+       }
+       assert(nfound == oldnumofslots);
+       SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));
+}
+
+RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)
+{
+       RefNode *t = _buckets[mainpos];
+       RefNode *newnode = _freelist;
+       newnode->obj = obj;
+       _buckets[mainpos] = newnode;
+       _freelist = _freelist->next;
+       newnode->next = t;
+       assert(newnode->refs == 0);
+       _slotused++;
+       return newnode;
+}
+
+RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)
+{
+       RefNode *ref;
+       mainpos = ::HashObj(obj)&(_numofslots-1);
+       *prev = NULL;
+       for (ref = _buckets[mainpos]; ref; ) {
+               if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))
+                       break;
+               *prev = ref;
+               ref = ref->next;
+       }
+       if(ref == NULL && add) {
+               if(_numofslots == _slotused) {
+                       assert(_freelist == 0);
+                       Resize(_numofslots*2);
+                       mainpos = ::HashObj(obj)&(_numofslots-1);
+               }
+               ref = Add(mainpos,obj);
+       }
+       return ref;
+}
+
+void RefTable::AllocNodes(SQUnsignedInteger size)
+{
+       RefNode **bucks;
+       RefNode *nodes;
+       bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));
+       nodes = (RefNode *)&bucks[size];
+       RefNode *temp = nodes;
+       SQUnsignedInteger n;
+       for(n = 0; n < size - 1; n++) {
+               bucks[n] = NULL;
+               temp->refs = 0;
+               new (&temp->obj) SQObjectPtr;
+               temp->next = temp+1;
+               temp++;
+       }
+       bucks[n] = NULL;
+       temp->refs = 0;
+       new (&temp->obj) SQObjectPtr;
+       temp->next = NULL;
+       _freelist = nodes;
+       _nodes = nodes;
+       _buckets = bucks;
+       _slotused = 0;
+       _numofslots = size;
+}
+//////////////////////////////////////////////////////////////////////////
+//StringTable
+/*
+* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
+* http://www.lua.org/copyright.html#4
+* http://www.lua.org/source/4.0.1/src_lstring.c.html
+*/
+
+StringTable::StringTable()
+{
+       AllocNodes(4);
+       _slotused = 0;
+}
+
+StringTable::~StringTable()
+{
+       SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
+       _strings = NULL;
+}
+
+void StringTable::AllocNodes(SQInteger size)
+{
+       _numofslots = size;
+       _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
+       memset(_strings,0,sizeof(SQString*)*_numofslots);
+}
+
+SQString *StringTable::Add(const SQChar *news,SQInteger len)
+{
+       if(len<0)
+               len = (SQInteger)scstrlen(news);
+       SQHash h = ::_hashstr(news,len)&(_numofslots-1);
+       SQString *s;
+       for (s = _strings[h]; s; s = s->_next){
+               if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))
+                       return s; //found
+       }
+
+       SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));
+       new (t) SQString;
+       memcpy(t->_val,news,rsl(len));
+       t->_val[len] = _SC('\0');
+       t->_len = len;
+       t->_hash = ::_hashstr(news,len);
+       t->_next = _strings[h];
+       _strings[h] = t;
+       _slotused++;
+       if (_slotused > _numofslots)  /* too crowded? */
+               Resize(_numofslots*2);
+       return t;
+}
+
+void StringTable::Resize(SQInteger size)
+{
+       SQInteger oldsize=_numofslots;
+       SQString **oldtable=_strings;
+       AllocNodes(size);
+       for (SQInteger i=0; i<oldsize; i++){
+               SQString *p = oldtable[i];
+               while(p){
+                       SQString *next = p->_next;
+                       SQHash h = p->_hash&(_numofslots-1);
+                       p->_next = _strings[h];
+                       _strings[h] = p;
+                       p = next;
+               }
+       }
+       SQ_FREE(oldtable,oldsize*sizeof(SQString*));
+}
+
+void StringTable::Remove(SQString *bs)
+{
+       SQString *s;
+       SQString *prev=NULL;
+       SQHash h = bs->_hash&(_numofslots - 1);
+       
+       for (s = _strings[h]; s; ){
+               if(s == bs){
+                       if(prev)
+                               prev->_next = s->_next;
+                       else
+                               _strings[h] = s->_next;
+                       _slotused--;
+                       SQInteger slen = s->_len;
+                       s->~SQString();
+                       SQ_FREE(s,sizeof(SQString) + rsl(slen));
+                       return;
+               }
+               prev = s;
+               s = s->_next;
+       }
+       assert(0);//if this fail something is wrong
+}
diff --git a/src/squirrel/squirrel/sqstate.h b/src/squirrel/squirrel/sqstate.h
new file mode 100644 (file)
index 0000000..fb67665
--- /dev/null
@@ -0,0 +1,142 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTATE_H_
+#define _SQSTATE_H_
+
+#include "squtils.h"
+#include "sqobject.h"
+struct SQString;
+struct SQTable;
+//max number of character for a printed number
+#define NUMBER_MAX_CHAR 50
+
+struct StringTable
+{
+       StringTable();
+       ~StringTable();
+       SQString *Add(const SQChar *,SQInteger len);
+       void Remove(SQString *);
+private:
+       void Resize(SQInteger size);
+       void AllocNodes(SQInteger size);
+       SQString **_strings;
+       SQUnsignedInteger _numofslots;
+       SQUnsignedInteger _slotused;
+};
+
+struct RefTable {
+       struct RefNode {
+               SQObjectPtr obj;
+               SQUnsignedInteger refs;
+               struct RefNode *next;
+       };
+       RefTable();
+       ~RefTable();
+       void AddRef(SQObject &obj);
+       SQBool Release(SQObject &obj);
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+#endif
+       void Finalize();
+private:
+       RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add);
+       RefNode *Add(SQHash mainpos,SQObject &obj);
+       void Resize(SQUnsignedInteger size);
+       void AllocNodes(SQUnsignedInteger size);
+       SQUnsignedInteger _numofslots;
+       SQUnsignedInteger _slotused;
+       RefNode *_nodes;
+       RefNode *_freelist;
+       RefNode **_buckets;
+};
+
+#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len)
+#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr)
+
+struct SQObjectPtr;
+
+struct SQSharedState
+{
+       SQSharedState();
+       ~SQSharedState();
+       void Init();
+public:
+       SQChar* GetScratchPad(SQInteger size);
+       SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
+#ifndef NO_GARBAGE_COLLECTOR
+       SQInteger CollectGarbage(SQVM *vm); 
+       static void MarkObject(SQObjectPtr &o,SQCollectable **chain);
+#endif
+       SQObjectPtrVec *_metamethods;
+       SQObjectPtr _metamethodsmap;
+       SQObjectPtrVec *_systemstrings;
+       SQObjectPtrVec *_types;
+       StringTable *_stringtable;
+       RefTable _refs_table;
+       SQObjectPtr _registry;
+       SQObjectPtr _constructoridx;
+#ifndef NO_GARBAGE_COLLECTOR
+       SQCollectable *_gc_chain;
+#endif
+       SQObjectPtr _root_vm;
+       SQObjectPtr _table_default_delegate;
+       static SQRegFunction _table_default_delegate_funcz[];
+       SQObjectPtr _array_default_delegate;
+       static SQRegFunction _array_default_delegate_funcz[];
+       SQObjectPtr _string_default_delegate;
+       static SQRegFunction _string_default_delegate_funcz[];
+       SQObjectPtr _number_default_delegate;
+       static SQRegFunction _number_default_delegate_funcz[];
+       SQObjectPtr _generator_default_delegate;
+       static SQRegFunction _generator_default_delegate_funcz[];
+       SQObjectPtr _closure_default_delegate;
+       static SQRegFunction _closure_default_delegate_funcz[];
+       SQObjectPtr _thread_default_delegate;
+       static SQRegFunction _thread_default_delegate_funcz[];
+       SQObjectPtr _class_default_delegate;
+       static SQRegFunction _class_default_delegate_funcz[];
+       SQObjectPtr _instance_default_delegate;
+       static SQRegFunction _instance_default_delegate_funcz[];
+       SQObjectPtr _weakref_default_delegate;
+       static SQRegFunction _weakref_default_delegate_funcz[];
+       
+       SQCOMPILERERROR _compilererrorhandler;
+       SQPRINTFUNCTION _printfunc;
+       bool _debuginfo;
+       bool _notifyallexceptions;
+private:
+       SQChar *_scratchpad;
+       SQInteger _scratchpadsize;
+};
+
+#define _sp(s) (_sharedstate->GetScratchPad(s))
+#define _spval (_sharedstate->GetScratchPad(-1))
+
+#define _table_ddel            _table(_sharedstate->_table_default_delegate) 
+#define _array_ddel            _table(_sharedstate->_array_default_delegate) 
+#define _string_ddel   _table(_sharedstate->_string_default_delegate) 
+#define _number_ddel   _table(_sharedstate->_number_default_delegate) 
+#define _generator_ddel        _table(_sharedstate->_generator_default_delegate) 
+#define _closure_ddel  _table(_sharedstate->_closure_default_delegate) 
+#define _thread_ddel   _table(_sharedstate->_thread_default_delegate) 
+#define _class_ddel            _table(_sharedstate->_class_default_delegate) 
+#define _instance_ddel _table(_sharedstate->_instance_default_delegate) 
+#define _weakref_ddel  _table(_sharedstate->_weakref_default_delegate) 
+
+#ifdef SQUNICODE //rsl REAL STRING LEN
+#define rsl(l) ((l)<<1)
+#else
+#define rsl(l) (l)
+#endif
+
+extern SQObjectPtr _null_;
+extern SQObjectPtr _true_;
+extern SQObjectPtr _false_;
+extern SQObjectPtr _one_;
+extern SQObjectPtr _minusone_;
+
+bool CompileTypemask(SQIntVec &res,const SQChar *typemask);
+
+void *sq_vm_malloc(SQUnsignedInteger size);
+void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size);
+void sq_vm_free(void *p,SQUnsignedInteger size);
+#endif //_SQSTATE_H_
diff --git a/src/squirrel/squirrel/sqstring.h b/src/squirrel/squirrel/sqstring.h
new file mode 100644 (file)
index 0000000..14f09e1
--- /dev/null
@@ -0,0 +1,31 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQSTRING_H_
+#define _SQSTRING_H_
+
+inline SQHash _hashstr (const SQChar *s, size_t l)
+{
+               SQHash h = (SQHash)l;  /* seed */
+               size_t step = (l>>5)|1;  /* if string is too long, don't hash all its chars */
+               for (; l>=step; l-=step)
+                       h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++));
+               return h;
+}
+
+struct SQString : public SQRefCounted
+{
+       SQString(){}
+       ~SQString(){}
+public:
+       static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 );
+       SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+       void Release();
+       SQSharedState *_sharedstate;
+       SQString *_next; //chain for the string table
+       SQInteger _len;
+       SQHash _hash;
+       SQChar _val[1];
+};
+
+
+
+#endif //_SQSTRING_H_
diff --git a/src/squirrel/squirrel/sqtable.cpp b/src/squirrel/squirrel/sqtable.cpp
new file mode 100644 (file)
index 0000000..1b3acd0
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqtable.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+
+SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize)
+{
+       SQInteger pow2size=MINPOWER2;
+       while(nInitialSize>pow2size)pow2size=pow2size<<1;
+       AllocNodes(pow2size);
+       _usednodes = 0;
+       _delegate = NULL;
+       INIT_CHAIN();
+       ADD_TO_CHAIN(&_sharedstate->_gc_chain,this);
+}
+
+void SQTable::Remove(const SQObjectPtr &key)
+{
+       
+       _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+       if (n) {
+               n->val = n->key = _null_;
+               _usednodes--;
+               Rehash(false);
+       }
+}
+
+void SQTable::AllocNodes(SQInteger nSize)
+{
+       _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize);
+       for(SQInteger i=0;i<nSize;i++){
+               new (&nodes[i]) _HashNode;
+               nodes[i].next=NULL;
+       }
+       _numofnodes=nSize;
+       _nodes=nodes;
+       _firstfree=&_nodes[_numofnodes-1];
+}
+
+void SQTable::Rehash(bool force)
+{
+       SQInteger oldsize=_numofnodes;
+       //prevent problems with the integer division
+       if(oldsize<4)oldsize=4;
+       _HashNode *nold=_nodes;
+       SQInteger nelems=CountUsed();
+       if (nelems >= oldsize-oldsize/4)  /* using more than 3/4? */
+               AllocNodes(oldsize*2);
+       else if (nelems <= oldsize/4 &&  /* less than 1/4? */
+               oldsize > MINPOWER2)
+               AllocNodes(oldsize/2);
+       else if(force)
+               AllocNodes(oldsize);
+       else
+               return;
+       _usednodes = 0;
+       for (SQInteger i=0; i<oldsize; i++) {
+               _HashNode *old = nold+i;
+               if (type(old->key) != OT_NULL)
+                       NewSlot(old->key,old->val);
+       }
+       for(SQInteger k=0;k<oldsize;k++) 
+               nold[k].~_HashNode();
+       SQ_FREE(nold,oldsize*sizeof(_HashNode));
+}
+
+SQTable *SQTable::Clone()
+{
+       SQTable *nt=Create(_opt_ss(this),_numofnodes);
+       SQInteger ridx=0;
+       SQObjectPtr key,val;
+       while((ridx=Next(true,ridx,key,val))!=-1){
+               nt->NewSlot(key,val);
+       }
+       nt->SetDelegate(_delegate);
+       return nt;
+}
+
+bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val)
+{
+       if(type(key) == OT_NULL)
+               return false;
+       _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+       if (n) {
+               val = _realval(n->val);
+               return true;
+       }
+       return false;
+}
+bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val)
+{
+       assert(type(key) != OT_NULL);
+       SQHash h = HashObj(key) & (_numofnodes - 1);
+       _HashNode *n = _Get(key, h);
+       if (n) {
+               n->val = val;
+               return false;
+       }
+       _HashNode *mp = &_nodes[h];
+       n = mp;
+
+
+       //key not found I'll insert it
+       //main pos is not free
+
+       if(type(mp->key) != OT_NULL) {
+               n = _firstfree;  /* get a free place */
+               SQHash mph = HashObj(mp->key) & (_numofnodes - 1);
+               _HashNode *othern;  /* main position of colliding node */
+               
+               if (mp > n && (othern = &_nodes[mph]) != mp){
+                       /* yes; move colliding node into free position */
+                       while (othern->next != mp){
+                               assert(othern->next != NULL);
+                               othern = othern->next;  /* find previous */
+                       }
+                       othern->next = n;  /* redo the chain with `n' in place of `mp' */
+                       n->key = mp->key;
+                       n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */
+                       n->next = mp->next;
+                       mp->key = _null_;
+                       mp->val = _null_;
+                       mp->next = NULL;  /* now `mp' is free */
+               }
+               else{
+                       /* new node will go into free position */
+                       n->next = mp->next;  /* chain new position */
+                       mp->next = n;
+                       mp = n;
+               }
+       }
+       mp->key = key;
+
+       for (;;) {  /* correct `firstfree' */
+               if (type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) {
+                       mp->val = val;
+                       _usednodes++;
+                       return true;  /* OK; table still has a free place */
+               }
+               else if (_firstfree == _nodes) break;  /* cannot decrement from here */
+               else (_firstfree)--;
+       }
+       Rehash(true);
+       return NewSlot(key, val);
+}
+
+SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+       SQInteger idx = (SQInteger)TranslateIndex(refpos);
+       while (idx < _numofnodes) {
+               if(type(_nodes[idx].key) != OT_NULL) {
+                       //first found
+                       _HashNode &n = _nodes[idx];
+                       outkey = n.key;
+                       outval = getweakrefs?(SQObject)n.val:_realval(n.val);
+                       //return idx for the next iteration
+                       return ++idx;
+               }
+               ++idx;
+       }
+       //nothing to iterate anymore
+       return -1;
+}
+
+
+bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val)
+{
+       _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+       if (n) {
+               n->val = val;
+               return true;
+       }
+       return false;
+}
+
+void SQTable::_ClearNodes()
+{
+       for(SQInteger i = 0;i < _numofnodes; i++) { _nodes[i].key = _null_; _nodes[i].val = _null_; }
+}
+
+void SQTable::Finalize()
+{
+       _ClearNodes();
+       SetDelegate(NULL);
+}
+
+void SQTable::Clear()
+{
+       _ClearNodes();
+       _usednodes = 0;
+       Rehash(true);
+}
diff --git a/src/squirrel/squirrel/sqtable.h b/src/squirrel/squirrel/sqtable.h
new file mode 100644 (file)
index 0000000..67a88de
--- /dev/null
@@ -0,0 +1,91 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQTABLE_H_
+#define _SQTABLE_H_
+/*
+* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
+* http://www.lua.org/copyright.html#4
+* http://www.lua.org/source/4.0.1/src_ltable.c.html
+*/
+
+#include "sqstring.h"
+
+
+#define hashptr(p)  ((SQHash)(((SQInteger)p) >> 3))
+
+inline SQHash HashObj(const SQObjectPtr &key)
+{
+       switch(type(key)) {
+               case OT_STRING:         return _string(key)->_hash;
+               case OT_FLOAT:          return (SQHash)((SQInteger)_float(key));
+               case OT_BOOL: case OT_INTEGER:  return (SQHash)((SQInteger)_integer(key));
+               default:                        return hashptr(key._unVal.pRefCounted);
+       }
+}
+
+struct SQTable : public SQDelegable 
+{
+private:
+       struct _HashNode
+       {
+               _HashNode() { next = NULL; }
+               SQObjectPtr val;
+               SQObjectPtr key;
+               _HashNode *next;
+       };
+       _HashNode *_firstfree;
+       _HashNode *_nodes;
+       SQInteger _numofnodes;
+       SQInteger _usednodes;
+       
+///////////////////////////
+       void AllocNodes(SQInteger nSize);
+       void Rehash(bool force);
+       SQTable(SQSharedState *ss, SQInteger nInitialSize);
+       void _ClearNodes();
+public:
+       static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize)
+       {
+               SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable));
+               new (newtable) SQTable(ss, nInitialSize);
+               newtable->_delegate = NULL;
+               return newtable;
+       }
+       void Finalize();
+       SQTable *Clone();
+       ~SQTable()
+       {
+               SetDelegate(NULL);
+               REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+               for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode();
+               SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode));
+       }
+#ifndef NO_GARBAGE_COLLECTOR 
+       void Mark(SQCollectable **chain);
+#endif
+       inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash)
+       {
+               _HashNode *n = &_nodes[hash];
+               do{
+                       if(_rawval(n->key) == _rawval(key) && type(n->key) == type(key)){
+                               return n;
+                       }
+               }while((n = n->next));
+               return NULL;
+       }
+       bool Get(const SQObjectPtr &key,SQObjectPtr &val);
+       void Remove(const SQObjectPtr &key);
+       bool Set(const SQObjectPtr &key, const SQObjectPtr &val);
+       //returns true if a new slot has been created false if it was already present
+       bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val);
+       SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+       
+       SQInteger CountUsed(){ return _usednodes;}
+       void Clear();
+       void Release()
+       {
+               sq_delete(this, SQTable);
+       }
+       
+};
+
+#endif //_SQTABLE_H_
diff --git a/src/squirrel/squirrel/squserdata.h b/src/squirrel/squirrel/squserdata.h
new file mode 100644 (file)
index 0000000..d192559
--- /dev/null
@@ -0,0 +1,38 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQUSERDATA_H_
+#define _SQUSERDATA_H_
+
+struct SQUserData : SQDelegable
+{
+       SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); }
+       ~SQUserData()
+       {
+               REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
+               SetDelegate(NULL);
+       }
+       static SQUserData* Create(SQSharedState *ss, SQInteger size)
+       {
+               SQUserData* ud = (SQUserData*)SQ_MALLOC(sizeof(SQUserData)+(size-1));
+               new (ud) SQUserData(ss);
+               ud->_size = size;
+               ud->_typetag = 0;
+               return ud;
+       }
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+       void Finalize(){SetDelegate(NULL);}
+#endif
+       void Release() {
+               if (_hook) _hook(_val,_size);
+               SQInteger tsize = _size - 1;
+               this->~SQUserData();
+               SQ_FREE(this, sizeof(SQUserData) + tsize);
+       }
+               
+       SQInteger _size;
+       SQRELEASEHOOK _hook;
+       SQUserPointer _typetag;
+       SQChar _val[1];
+};
+
+#endif //_SQUSERDATA_H_
diff --git a/src/squirrel/squirrel/squtils.h b/src/squirrel/squirrel/squtils.h
new file mode 100644 (file)
index 0000000..77ccdb2
--- /dev/null
@@ -0,0 +1,104 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQUTILS_H_
+#define _SQUTILS_H_
+
+#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;}
+#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));}
+#define SQ_MALLOC(__size) sq_vm_malloc((__size));
+#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size));
+#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size));
+
+//sqvector mini vector class, supports objects by value
+template<typename T> class sqvector
+{
+public:
+       sqvector()
+       {
+               _vals = NULL;
+               _size = 0;
+               _allocated = 0;
+       }
+       sqvector(const sqvector<T>& v)
+       {
+               copy(v);
+       }
+       void copy(const sqvector<T>& v)
+       {
+               resize(v._size);
+               for(SQUnsignedInteger i = 0; i < v._size; i++) {
+                       new ((void *)&_vals[i]) T(v._vals[i]);
+               }
+               _size = v._size;
+       }
+       ~sqvector()
+       {
+               if(_allocated) {
+                       for(SQUnsignedInteger i = 0; i < _size; i++)
+                               _vals[i].~T();
+                       SQ_FREE(_vals, (_allocated * sizeof(T)));
+               }
+       }
+       void reserve(SQUnsignedInteger newsize) { _realloc(newsize); }
+       void resize(SQUnsignedInteger newsize, const T& fill = T())
+       {
+               if(newsize > _allocated)
+                       _realloc(newsize);
+               if(newsize > _size) {
+                       while(_size < newsize) {
+                               new ((void *)&_vals[_size]) T(fill);
+                               _size++;
+                       }
+               }
+               else{
+                       for(SQUnsignedInteger i = newsize; i < _size; i++) {
+                               _vals[i].~T();
+                       }
+                       _size = newsize;
+               }
+       }
+       void shrinktofit() { if(_size > 4) { _realloc(_size); } }
+       T& top() const { return _vals[_size - 1]; }
+       inline SQUnsignedInteger size() const { return _size; }
+       bool empty() const { return (_size <= 0); }
+       inline T &push_back(const T& val = T())
+       {
+               if(_allocated <= _size)
+                       _realloc(_size * 2);
+               return *(new ((void *)&_vals[_size++]) T(val));
+       }
+       inline void pop_back()
+       {
+               _size--; _vals[_size].~T();
+       }
+       void insert(SQUnsignedInteger idx, const T& val)
+       {
+               resize(_size + 1);
+               for(SQUnsignedInteger i = _size - 1; i > idx; i--) {
+                       _vals[i] = _vals[i - 1];
+               }
+       _vals[idx] = val;
+       }
+       void remove(SQUnsignedInteger idx)
+       {
+               _vals[idx].~T();
+               if(idx < (_size - 1)) {
+                       memcpy(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1));
+               }
+               _size--;
+       }
+       SQUnsignedInteger capacity() { return _allocated; }
+       inline T &back() const { return _vals[_size - 1]; }
+       inline T& operator[](SQUnsignedInteger pos) const{ return _vals[pos]; }
+       T* _vals;
+private:
+       void _realloc(SQUnsignedInteger newsize)
+       {
+               newsize = (newsize > 0)?newsize:4;
+               _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T));
+               _allocated = newsize;
+       }
+       SQUnsignedInteger _size;
+       SQUnsignedInteger _allocated;
+};
+
+#endif //_SQUTILS_H_
diff --git a/src/squirrel/squirrel/sqvm.cpp b/src/squirrel/squirrel/sqvm.cpp
new file mode 100644 (file)
index 0000000..25ebcf9
--- /dev/null
@@ -0,0 +1,1495 @@
+/*
+       see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <math.h>
+#include <stdlib.h>
+#include "sqopcodes.h"
+#include "sqfuncproto.h"
+#include "sqvm.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "squserdata.h"
+#include "sqarray.h"
+#include "sqclass.h"
+
+#define TOP() (_stack._vals[_top-1])
+
+bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+       SQInteger res;
+       SQInteger i1 = _integer(o1), i2 = _integer(o2);
+       if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER))
+       {
+               switch(op) {
+                       case BW_AND:    res = i1 & i2; break;
+                       case BW_OR:             res = i1 | i2; break;
+                       case BW_XOR:    res = i1 ^ i2; break;
+                       case BW_SHIFTL: res = i1 << i2; break;
+                       case BW_SHIFTR: res = i1 >> i2; break;
+                       case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break;
+                       default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; }
+               }
+       } 
+       else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;}
+       trg = res;
+       return true;
+}
+
+bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+       if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+                       if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER)) {
+                               SQInteger res, i1 = _integer(o1), i2 = _integer(o2);
+                               switch(op) {
+                               case '+': res = i1 + i2; break;
+                               case '-': res = i1 - i2; break;
+                               case '/': if(i2 == 0) { Raise_Error(_SC("division by zero")); return false; }
+                                       res = i1 / i2; 
+                                       break;
+                               case '*': res = i1 * i2; break;
+                               case '%': res = i1 % i2; break;
+                               default: res = 0xDEADBEEF;
+                               }
+                               trg = res;
+                       }else{
+                               SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2);
+                               switch(op) {
+                               case '+': res = f1 + f2; break;
+                               case '-': res = f1 - f2; break;
+                               case '/': res = f1 / f2; break;
+                               case '*': res = f1 * f2; break;
+                               case '%': res = SQFloat(fmod((double)f1,(double)f2)); break;
+                               default: res = 0x0f;
+                               }
+                               trg = res;
+                       }       
+               } else {
+                       if(op == '+' && (type(o1) == OT_STRING || type(o2) == OT_STRING)){
+                                       if(!StringCat(o1, o2, trg)) return false;
+                       }
+                       else if(!ArithMetaMethod(op,o1,o2,trg)) { 
+                               Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false; 
+                       }
+               }
+               return true;
+}
+
+SQVM::SQVM(SQSharedState *ss)
+{
+       _sharedstate=ss;
+       _suspended = SQFalse;
+       _suspended_target=-1;
+       _suspended_root = SQFalse;
+       _suspended_traps=-1;
+       _foreignptr=NULL;
+       _nnativecalls=0;
+       _lasterror = _null_;
+       _errorhandler = _null_;
+       _debughook = _null_;
+       ci = NULL;
+       INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+void SQVM::Finalize()
+{
+       _roottable = _null_;
+       _lasterror = _null_;
+       _errorhandler = _null_;
+       _debughook = _null_;
+       temp_reg = _null_;
+       SQInteger size=_stack.size();
+       for(SQInteger i=0;i<size;i++)
+               _stack[i]=_null_;
+}
+
+SQVM::~SQVM()
+{
+       Finalize();
+       sq_free(_callsstack,_alloccallsstacksize*sizeof(CallInfo));
+       REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest)
+{
+       SQMetaMethod mm;
+       switch(op){
+               case _SC('+'): mm=MT_ADD; break;
+               case _SC('-'): mm=MT_SUB; break;
+               case _SC('/'): mm=MT_DIV; break;
+               case _SC('*'): mm=MT_MUL; break;
+               case _SC('%'): mm=MT_MODULO; break;
+               default: mm = MT_ADD; assert(0); break; //shutup compiler
+       }
+       if(is_delegable(o1) && _delegable(o1)->_delegate) {
+               Push(o1);Push(o2);
+               return CallMetaMethod(_delegable(o1),mm,2,dest);
+       }
+       return false;
+}
+
+bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o)
+{
+       
+       switch(type(o)) {
+       case OT_INTEGER:
+               trg = -_integer(o);
+               return true;
+       case OT_FLOAT:
+               trg = -_float(o);
+               return true;
+       case OT_TABLE:
+       case OT_USERDATA:
+       case OT_INSTANCE:
+               if(_delegable(o)->_delegate) {
+                       Push(o);
+                       if(CallMetaMethod(_delegable(o), MT_UNM, 1, temp_reg)) {
+                               trg = temp_reg;
+                               return true;
+                       }
+               }
+       default:break; //shutup compiler
+       }
+       Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o));
+       return false;
+}
+
+#define _RET_SUCCEED(exp) { result = (exp); return true; } 
+bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result)
+{
+       if(type(o1)==type(o2)){
+               if(_userpointer(o1)==_userpointer(o2))_RET_SUCCEED(0);
+               SQObjectPtr res;
+               switch(type(o1)){
+               case OT_STRING:
+                       _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2)));
+               case OT_INTEGER:
+                       _RET_SUCCEED(_integer(o1)-_integer(o2));
+               case OT_FLOAT:
+                       _RET_SUCCEED((_float(o1)<_float(o2))?-1:1);
+               case OT_TABLE:
+               case OT_USERDATA:
+               case OT_INSTANCE:
+                       if(_delegable(o1)->_delegate) {
+                               Push(o1);Push(o2);
+                               if(CallMetaMethod(_delegable(o1),MT_CMP,2,res)) break;
+                       }
+                       //continues through (no break needed)
+               default: 
+                       _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 );
+               }
+               if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; }
+                       _RET_SUCCEED(_integer(res));
+               
+       }
+       else{
+               if(sq_isnumeric(o1) && sq_isnumeric(o2)){
+                       if((type(o1)==OT_INTEGER) && (type(o2)==OT_FLOAT)) { 
+                               if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); }
+                               else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); }
+                               _RET_SUCCEED(1);
+                       }
+                       else{
+                               if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); }
+                               else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); }
+                               _RET_SUCCEED(1);
+                       }
+               }
+               else if(type(o1)==OT_NULL) {_RET_SUCCEED(-1);}
+               else if(type(o2)==OT_NULL) {_RET_SUCCEED(1);}
+               else { Raise_CompareError(o1,o2); return false; }
+               
+       }
+       assert(0);
+       _RET_SUCCEED(0); //cannot happen
+}
+
+bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res)
+{
+       SQInteger r;
+       if(ObjCmp(o1,o2,r)) {
+               switch(op) {
+                       case CMP_G: res = (r > 0)?_true_:_false_; return true;
+                       case CMP_GE: res = (r >= 0)?_true_:_false_; return true;
+                       case CMP_L: res = (r < 0)?_true_:_false_; return true;
+                       case CMP_LE: res = (r <= 0)?_true_:_false_; return true;
+                       
+               }
+               assert(0);
+       }
+       return false;
+}
+
+void SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res)
+{
+       switch(type(o)) {
+       case OT_STRING:
+               res = o;
+               return;
+       case OT_FLOAT:
+               scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)),_SC("%g"),_float(o));
+               break;
+       case OT_INTEGER:
+               scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)),_SC("%d"),_integer(o));
+               break;
+       case OT_BOOL:
+               scsprintf(_sp(rsl(6)),_integer(o)?_SC("true"):_SC("false"));
+               break;
+       case OT_TABLE:
+       case OT_USERDATA:
+       case OT_INSTANCE:
+               if(_delegable(o)->_delegate) {
+                       Push(o);
+                       if(CallMetaMethod(_delegable(o),MT_TOSTRING,1,res)) {
+                               if(type(res) == OT_STRING)
+                                       return;
+                               //else keeps going to the default
+                       }
+               }
+       default:
+               scsprintf(_sp(rsl(sizeof(void*)+20)),_SC("(%s : 0x%p)"),GetTypeName(o),(void*)_rawval(o));
+       }
+       res = SQString::Create(_ss(this),_spval);
+}
+
+
+bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest)
+{
+       SQObjectPtr a, b;
+       ToString(str, a);
+       ToString(obj, b);
+       SQInteger l = _string(a)->_len , ol = _string(b)->_len;
+       SQChar *s = _sp(rsl(l + ol + 1));
+       memcpy(s, _stringval(a), rsl(l)); 
+       memcpy(s + l, _stringval(b), rsl(ol));
+       dest = SQString::Create(_ss(this), _spval, l + ol);
+       return true;
+}
+
+const SQChar *IdType2Name(SQObjectType type)
+{
+       switch(_RAW_TYPE(type))
+       {
+       case _RT_NULL:return _SC("null");
+       case _RT_INTEGER:return _SC("integer");
+       case _RT_FLOAT:return _SC("float");
+       case _RT_BOOL:return _SC("bool");
+       case _RT_STRING:return _SC("string");
+       case _RT_TABLE:return _SC("table");
+       case _RT_ARRAY:return _SC("array");
+       case _RT_GENERATOR:return _SC("generator");
+       case _RT_CLOSURE:
+       case _RT_NATIVECLOSURE:
+               return _SC("function");
+       case _RT_USERDATA:
+       case _RT_USERPOINTER:
+               return _SC("userdata");
+       case _RT_THREAD: return _SC("thread");
+       case _RT_FUNCPROTO: return _SC("function");
+       case _RT_CLASS: return _SC("class");
+       case _RT_INSTANCE: return _SC("instance");
+       case _RT_WEAKREF: return _SC("weakref");
+       default:
+               return NULL;
+       }
+}
+
+const SQChar *GetTypeName(const SQObjectPtr &obj1)
+{
+       return IdType2Name(type(obj1)); 
+}
+
+void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)
+{
+       if(is_delegable(obj1) && _delegable(obj1)->_delegate) {
+               Push(obj1);
+               if(CallMetaMethod(_delegable(obj1),MT_TYPEOF,1,dest))
+                       return;
+       }
+       dest = SQString::Create(_ss(this),GetTypeName(obj1));
+}
+
+bool SQVM::Init(SQVM *friendvm, SQInteger stacksize)
+{
+       _stack.resize(stacksize);
+       //_callsstack.reserve(4);
+       _alloccallsstacksize = 4;
+       _callsstacksize = 0;
+       _callsstack = (CallInfo*)sq_malloc(_alloccallsstacksize*sizeof(CallInfo));
+       _stackbase = 0;
+       _top = 0;
+       if(!friendvm) 
+               _roottable = SQTable::Create(_ss(this), 0);
+       else {
+               _roottable = friendvm->_roottable;
+               _errorhandler = friendvm->_errorhandler;
+               _debughook = friendvm->_debughook;
+       }
+       
+       sq_base_register(this);
+       return true;
+}
+
+extern SQInstructionDesc g_InstrDesc[];
+
+bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteger stackbase,bool tailcall)
+{
+       SQFunctionProto *func = _funcproto(closure->_function);
+       
+       const SQInteger paramssize = func->_nparameters;
+       const SQInteger newtop = stackbase + func->_stacksize;
+       
+       
+       if (paramssize != nargs) {
+               if(func->_varparams)
+               {
+                       if (nargs < paramssize) {
+                               Raise_Error(_SC("wrong number of parameters"));
+                               return false;
+                       }
+                       for(SQInteger n = 0; n < nargs - paramssize; n++) {
+                               _vargsstack.push_back(_stack._vals[stackbase+paramssize+n]);
+                               _stack._vals[stackbase+paramssize+n] = _null_;
+                       }
+               }
+               else {
+                       Raise_Error(_SC("wrong number of parameters"));
+                       return false;
+               }
+       }
+
+       if(type(closure->_env) == OT_WEAKREF) {
+               _stack._vals[stackbase] = _weakref(closure->_env)->_obj;
+       }
+
+       if (!tailcall) {
+               CallInfo lc;
+               lc._etraps = 0;
+               lc._prevstkbase = (SQInt32) ( stackbase - _stackbase );
+               lc._target = (SQInt32) target;
+               lc._prevtop = (SQInt32) (_top - _stackbase);
+               lc._ncalls = 1;
+               lc._root = SQFalse;
+               PUSH_CALLINFO(this, lc);
+       }
+       else {
+               ci->_ncalls++;
+       }
+       ci->_vargs.size = (SQInt32)(nargs - paramssize);
+       ci->_vargs.base = (SQInt32) (_vargsstack.size()-(ci->_vargs.size));
+       ci->_closure._unVal.pClosure = closure;
+       ci->_closure._type = OT_CLOSURE;
+       ci->_literals = func->_literals;
+       ci->_ip = func->_instructions;
+       //grows the stack if needed
+       if (((SQUnsignedInteger)newtop + (func->_stacksize<<1)) > _stack.size()) {
+               _stack.resize(_stack.size() + (func->_stacksize<<1));
+       }
+               
+       _top = newtop;
+       _stackbase = stackbase;
+       return true;
+}
+
+bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval)
+{
+       if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+               for(SQInteger i=0;i<ci->_ncalls;i++)
+                       CallDebugHook(_SC('r'));
+                       
+       SQBool broot = ci->_root;
+       SQInteger last_top = _top;
+       SQInteger target = ci->_target;
+       SQInteger oldstackbase = _stackbase;
+       _stackbase -= ci->_prevstkbase;
+       _top = _stackbase + ci->_prevtop;
+       if(ci->_vargs.size) PopVarArgs(ci->_vargs);
+       POP_CALLINFO(this);
+       if (broot) {
+               if (_arg0 != MAX_FUNC_STACKSIZE) retval = _stack._vals[oldstackbase+_arg1];
+               else retval = _null_;
+       }
+       else {
+               if(target != -1) { //-1 is when a class contructor ret value has to be ignored
+                       if (_arg0 != MAX_FUNC_STACKSIZE)
+                               STK(target) = _stack._vals[oldstackbase+_arg1];
+                       else
+                               STK(target) = _null_;
+               }
+       }
+
+       while (last_top >= _top) _stack._vals[last_top--].Null();
+       assert(oldstackbase >= _stackbase); 
+       return broot?true:false;
+}
+
+#define _RET_ON_FAIL(exp) { if(!exp) return false; }
+
+bool SQVM::LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+       _RET_ON_FAIL(ARITH_OP( op , target, a, incr));
+       a = target;
+       return true;
+}
+
+bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+       SQObjectPtr trg;
+       _RET_ON_FAIL(ARITH_OP( op , trg, a, incr));
+       target = a;
+       a = trg;
+       return true;
+}
+
+bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix)
+{
+       SQObjectPtr tmp, tself = self, tkey = key;
+       if (!Get(tself, tkey, tmp, false, true)) { Raise_IdxError(tkey); return false; }
+       _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr))
+       Set(tself, tkey, target,true);
+       if (postfix) target = tmp;
+       return true;
+}
+
+#define arg0 (_i_._arg0)
+#define arg1 (_i_._arg1)
+#define sarg1 (*((SQInt32 *)&_i_._arg1))
+#define arg2 (_i_._arg2)
+#define arg3 (_i_._arg3)
+#define sarg3 ((SQInteger)*((signed char *)&_i_._arg3))
+
+SQRESULT SQVM::Suspend()
+{
+       if (_suspended)
+               return sq_throwerror(this, _SC("cannot suspend an already suspended vm"));
+       if (_nnativecalls!=2)
+               return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods"));
+       return SQ_SUSPEND_FLAG;
+}
+
+void SQVM::PopVarArgs(VarArgs &vargs)
+{
+       for(SQInteger n = 0; n< vargs.size; n++)
+               _vargsstack.pop_back();
+}
+
+#define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; }
+bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr 
+&o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump)
+{
+       SQInteger nrefidx;
+       switch(type(o1)) {
+       case OT_TABLE:
+               if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos);
+               o4 = (SQInteger)nrefidx; _FINISH(1);
+       case OT_ARRAY:
+               if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos);
+               o4 = (SQInteger) nrefidx; _FINISH(1);
+       case OT_STRING:
+               if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos);
+               o4 = (SQInteger)nrefidx; _FINISH(1);
+       case OT_CLASS:
+               if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos);
+               o4 = (SQInteger)nrefidx; _FINISH(1);
+       case OT_USERDATA:
+       case OT_INSTANCE:
+               if(_delegable(o1)->_delegate) {
+                       SQObjectPtr itr;
+                       Push(o1);
+                       Push(o4);
+                       if(CallMetaMethod(_delegable(o1), MT_NEXTI, 2, itr)){
+                               o4 = o2 = itr;
+                               if(type(itr) == OT_NULL) _FINISH(exitpos);
+                               if(!Get(o1, itr, o3, false,false)) {
+                                       Raise_Error(_SC("_nexti returned an invalid idx"));
+                                       return false;
+                               }
+                               _FINISH(1);
+                       }
+                       Raise_Error(_SC("_nexti failed"));
+                       return false;
+               }
+               break;
+       case OT_GENERATOR:
+               if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos);
+               if(_generator(o1)->_state == SQGenerator::eSuspended) {
+                       SQInteger idx = 0;
+                       if(type(o4) == OT_INTEGER) {
+                               idx = _integer(o4) + 1;
+                       }
+                       o2 = idx;
+                       o4 = idx;
+                       _generator(o1)->Resume(this, arg_2+1);
+                       _FINISH(0);
+               }
+       default: 
+               Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1));
+       }
+       return false; //cannot be hit(just to avoid warnings)
+}
+
+bool SQVM::DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2)
+{
+       if(type(o1) != OT_TABLE) { Raise_Error(_SC("delegating a '%s'"), GetTypeName(o1)); return false; }
+       switch(type(o2)) {
+       case OT_TABLE:
+               if(!_table(o1)->SetDelegate(_table(o2))){
+                       Raise_Error(_SC("delegate cycle detected"));
+                       return false;
+               }
+               break;
+       case OT_NULL:
+               _table(o1)->SetDelegate(NULL);
+               break;
+       default:
+               Raise_Error(_SC("using '%s' as delegate"), GetTypeName(o2));
+               return false;
+               break;
+       }
+       trg = o1;
+       return true;
+}
+#define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1))
+
+#define _GUARD(exp) { if(!exp) { Raise_Error(_lasterror); SQ_THROW();} }
+
+#define SQ_THROW() { goto exception_trap; }
+
+bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)
+{
+       SQInteger nouters;
+       SQClosure *closure = SQClosure::Create(_ss(this), func);
+       if((nouters = func->_noutervalues)) {
+               closure->_outervalues.reserve(nouters);
+               for(SQInteger i = 0; i<nouters; i++) {
+                       SQOuterVar &v = func->_outervalues[i];
+                       switch(v._type){
+                       case otSYMBOL:
+                               closure->_outervalues.push_back(_null_);
+                               if(!Get(_stack._vals[_stackbase]/*STK(0)*/, v._src, closure->_outervalues.top(), false,true))
+                               {Raise_IdxError(v._src); return false; }
+                               break;
+                       case otLOCAL:
+                               closure->_outervalues.push_back(_stack._vals[_stackbase+_integer(v._src)]);
+                               break;
+                       case otOUTER:
+                               closure->_outervalues.push_back(_closure(ci->_closure)->_outervalues[_integer(v._src)]);
+                               break;
+                       }
+               }
+       }
+       target = closure;
+       return true;
+
+}
+
+bool SQVM::GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &index,CallInfo *ci)
+{
+       if(ci->_vargs.size == 0) {
+               Raise_Error(_SC("the function doesn't have var args"));
+               return false;
+       }
+       if(!sq_isnumeric(index)){
+               Raise_Error(_SC("indexing 'vargv' with %s"),GetTypeName(index));
+               return false;
+       }
+       SQInteger idx = tointeger(index);
+       if(idx < 0 || idx >= ci->_vargs.size){ Raise_Error(_SC("vargv index out of range")); return false; }
+       target = _vargsstack[ci->_vargs.base+idx];
+       return true;
+}
+
+bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes)
+{
+       SQClass *base = NULL;
+       SQObjectPtr attrs;
+       if(baseclass != -1) {
+               if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; }
+               base = _class(_stack._vals[_stackbase + baseclass]);
+       }
+       if(attributes != MAX_FUNC_STACKSIZE) {
+               attrs = _stack._vals[_stackbase+attributes];
+       }
+       target = SQClass::Create(_ss(this),base);
+       if(type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) {
+               int nparams = 2;
+               SQObjectPtr ret;
+               Push(target); Push(attrs);
+               Call(_class(target)->_metamethods[MT_INHERITED],nparams,_top - nparams, ret, false);
+               Pop(nparams);
+       }
+       _class(target)->_attributes = attrs;
+       return true;
+}
+
+
+
+bool SQVM::IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res)
+{
+       if(type(o1) == type(o2)) {
+               res = ((_userpointer(o1) == _userpointer(o2)?true:false));
+       }
+       else {
+               if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+                       SQInteger cmpres;
+                       if(!ObjCmp(o1, o2,cmpres)) return false;
+                       res = (cmpres == 0);
+               }
+               else {
+                       res = false;
+               }
+       }
+       return true;
+}
+
+bool SQVM::IsFalse(SQObjectPtr &o)
+{
+       if((type(o) & SQOBJECT_CANBEFALSE) && ( (type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0)) )
+               || (_integer(o) == 0) ) { //OT_NULL|OT_INTEGER|OT_BOOL
+               return true;
+       }
+       return false;
+}
+
+bool SQVM::GETPARENT_OP(SQObjectPtr &o,SQObjectPtr &target)
+{
+       switch(type(o)) {
+               case OT_TABLE: target = _table(o)->_delegate?SQObjectPtr(_table(o)->_delegate):_null_;
+                       break;
+               case OT_CLASS: target = _class(o)->_base?_class(o)->_base:_null_;
+                       break;
+               default:
+                       Raise_Error(_SC("the %s type doesn't have a parent slot"), GetTypeName(o));
+                       return false;
+       }
+       return true;
+}
+
+bool SQVM::Execute(SQObjectPtr &closure, SQInteger target, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et)
+{
+       if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+       _nnativecalls++;
+       AutoDec ad(&_nnativecalls);
+       SQInteger traps = 0;
+       //temp_reg vars for OP_CALL
+       SQInteger ct_target;
+       SQInteger ct_stackbase;
+       bool ct_tailcall; 
+
+       switch(et) {
+               case ET_CALL: 
+                       if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) { 
+                               //call the handler if there are no calls in the stack, if not relies on the previous node
+                               if(ci == NULL) CallErrorHandler(_lasterror);
+                               return false;
+                       }
+                       ci->_root = SQTrue;
+                       break;
+               case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = SQTrue; traps += ci->_etraps; break;
+               case ET_RESUME_VM:
+                       traps = _suspended_traps;
+                       ci->_root = _suspended_root;
+                       ci->_vargs = _suspend_varargs;
+                       _suspended = SQFalse;
+                       break;
+       }
+       
+exception_restore:
+       //
+       {
+               for(;;)
+               {
+                       const SQInstruction &_i_ = *ci->_ip++;
+                       //dumpstack(_stackbase);
+                       //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3);
+                       switch(_i_.op)
+                       {
+                       case _OP_LINE:
+                               if(type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+                                       CallDebugHook(_SC('l'),arg1);
+                               continue;
+                       case _OP_LOAD: TARGET = ci->_literals[arg1]; continue;
+                       case _OP_LOADINT: TARGET = (SQInteger)arg1; continue;
+                       case _OP_LOADFLOAT: TARGET = *((SQFloat *)&arg1); continue;
+                       case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue;
+                       case _OP_TAILCALL:
+                               temp_reg = STK(arg1);
+                               if (type(temp_reg) == OT_CLOSURE){ 
+                                       ct_tailcall = true;
+                                       if(ci->_vargs.size) PopVarArgs(ci->_vargs);
+                                       for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);
+                                       ct_target = ci->_target;
+                                       ct_stackbase = _stackbase;
+                                       goto common_call;
+                               }
+                       case _OP_CALL: {
+                                       ct_tailcall = false;
+                                       ct_target = arg0;
+                                       temp_reg = STK(arg1);
+                                       ct_stackbase = _stackbase+arg2;
+
+common_call:
+                                       SQInteger last_top = _top;
+                                       switch (type(temp_reg)) {
+                                       case OT_CLOSURE:{
+                                               _GUARD(StartCall(_closure(temp_reg), ct_target, arg3, ct_stackbase, ct_tailcall));
+                                               if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {
+                                                       SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));
+                                                       _GUARD(gen->Yield(this));
+                                                       Return(1, ct_target, temp_reg);
+
+
+
+
+                                                       STK(ct_target) = gen;
+                                                       while (last_top >= _top) _stack._vals[last_top--].Null();
+                                                       continue;
+                                               }
+                                               if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+                                                       CallDebugHook(_SC('c'));
+                                               }
+                                               continue;
+                                       case OT_NATIVECLOSURE: {
+                                               bool suspend;
+                                               _GUARD(CallNative(_nativeclosure(temp_reg), arg3, ct_stackbase, temp_reg,suspend));
+                                               if(suspend){
+                                                       _suspended = SQTrue;
+                                                       _suspended_target = ct_target;
+                                                       _suspended_root = ci->_root;
+                                                       _suspended_traps = traps;
+                                                       _suspend_varargs = ci->_vargs;
+                                                       outres = temp_reg;
+                                                       return true;
+                                               }
+                                               if(ct_target != -1) { //skip return value for constructors
+                                                       STK(ct_target) = temp_reg;
+                                               }
+                                                                                  }
+                                               continue;
+                                       case OT_CLASS:{
+                                               SQObjectPtr inst;
+                                               _GUARD(CreateClassInstance(_class(temp_reg),inst,temp_reg));
+                                               STK(ct_target) = inst;
+                                               ct_target = -1; //fakes return value target so that is not overwritten by the constructor
+                                               if(type(temp_reg) != OT_NULL) {
+                                                       _stack._vals[ct_stackbase] = inst;
+                                                       goto common_call; //hard core spaghetti code(reissues the OP_CALL to invoke the constructor)
+                                               }
+                                               }
+                                               break;
+                                       case OT_TABLE:
+                                       case OT_USERDATA:
+                                       case OT_INSTANCE:
+                                               {
+                                               Push(temp_reg);
+                                               for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i));
+                                               if (_delegable(temp_reg) && CallMetaMethod(_delegable(temp_reg), MT_CALL, arg3+1, temp_reg)){
+                                                       STK(ct_target) = temp_reg;
+                                                       break;
+                                               }
+                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+                                               SQ_THROW();
+                                         }
+                                       default:
+                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+                                               SQ_THROW();
+                                       }
+                               }
+                                 continue;
+                       case _OP_PREPCALL:
+                       case _OP_PREPCALLK:
+                               {
+                                       SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1);
+                                       SQObjectPtr &o = STK(arg2);
+                                       if (!Get(o, key, temp_reg,false,true)) {
+                                               if(type(o) == OT_CLASS) { //hack?
+                                                       if(_class_ddel->Get(key,temp_reg)) {
+                                                               STK(arg3) = o;
+                                                               TARGET = temp_reg;
+                                                               continue;
+                                                       }
+                                               }
+                                               { Raise_IdxError(key); SQ_THROW();}
+                                       }
+
+                                       STK(arg3) = type(o) == OT_CLASS?STK(0):o;
+                                       TARGET = temp_reg;
+                               }
+                               continue;
+                       case _OP_GETK:
+                               if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, false,true)) { Raise_IdxError(ci->_literals[arg1]); SQ_THROW();}
+                               TARGET = temp_reg;
+                               continue;
+                       case _OP_MOVE: TARGET = STK(arg1); continue;
+                       case _OP_NEWSLOT:
+                               _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),false));
+                               if(arg0 != arg3) TARGET = STK(arg3);
+                               continue;
+                       case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue;
+                       case _OP_SET:
+                               if (!Set(STK(arg1), STK(arg2), STK(arg3),true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+                               if (arg0 != arg3) TARGET = STK(arg3);
+                               continue;
+                       case _OP_GET:
+                               if (!Get(STK(arg1), STK(arg2), temp_reg, false,true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+                               TARGET = temp_reg;
+                               continue;
+                       case _OP_EQ:{
+                               bool res;
+                               if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+                               TARGET = res?_true_:_false_;
+                               }continue;
+                       case _OP_NE:{ 
+                               bool res;
+                               if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+                               TARGET = (!res)?_true_:_false_;
+                               } continue;
+                       case _OP_ARITH: _GUARD(ARITH_OP( arg3 , temp_reg, STK(arg2), STK(arg1))); TARGET = temp_reg; continue;
+                       case _OP_BITW:  _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue;
+                       case _OP_RETURN:
+                               if(type((ci)->_generator) == OT_GENERATOR) {
+                                       _generator((ci)->_generator)->Kill();
+                               }
+                               if(Return(arg0, arg1, temp_reg)){
+                                       assert(traps==0);
+                                       outres = temp_reg;
+                                       return true;
+                               }
+                               continue;
+                       case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n) = _null_; }continue;
+                       case _OP_LOADROOTTABLE: TARGET = _roottable; continue;
+                       case _OP_LOADBOOL: TARGET = arg1?_true_:_false_; continue;
+                       case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue;
+                       case _OP_JMP: ci->_ip += (sarg1); continue;
+                       case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+                       case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+                       case _OP_LOADFREEVAR: TARGET = _closure(ci->_closure)->_outervalues[arg1]; continue;
+                       case _OP_VARGC: TARGET = SQInteger(ci->_vargs.size); continue;
+                       case _OP_GETVARGV: 
+                               if(!GETVARGV_OP(TARGET,STK(arg1),ci)) { SQ_THROW(); } 
+                               continue;
+                       case _OP_NEWTABLE: TARGET = SQTable::Create(_ss(this), arg1); continue;
+                       case _OP_NEWARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue;
+                       case _OP_APPENDARRAY: _array(STK(arg0))->Append(COND_LITERAL);  continue;
+                       case _OP_GETPARENT: _GUARD(GETPARENT_OP(STK(arg1),TARGET)); continue;
+                       case _OP_COMPARITH: _GUARD(DerefInc(arg3, TARGET, STK((((SQUnsignedInteger)arg1&0xFFFF0000)>>16)), STK(arg2), STK(arg1&0x0000FFFF), false)); continue;
+                       case _OP_COMPARITHL: _GUARD(LOCAL_INC(arg3, TARGET, STK(arg1), STK(arg2))); continue;
+                       case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false));} continue;
+                       case _OP_INCL: {SQObjectPtr o(sarg3); _GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+                       case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true));} continue;
+                       case _OP_PINCL: {SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+                       case _OP_CMP:   _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET))  continue;
+                       case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, true,false)?_true_:_false_;continue;
+                       case _OP_INSTANCEOF: 
+                               if(type(STK(arg1)) != OT_CLASS || type(STK(arg2)) != OT_INSTANCE)
+                               {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();}
+                               TARGET = _instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?_true_:_false_;
+                               continue;
+                       case _OP_AND: 
+                               if(IsFalse(STK(arg2))) {
+                                       TARGET = STK(arg2);
+                                       ci->_ip += (sarg1);
+                               }
+                               continue;
+                       case _OP_OR:
+                               if(!IsFalse(STK(arg2))) {
+                                       TARGET = STK(arg2);
+                                       ci->_ip += (sarg1);
+                               }
+                               continue;
+                       case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue;
+                       case _OP_NOT: TARGET = (IsFalse(STK(arg1))?_true_:_false_); continue;
+                       case _OP_BWNOT:
+                               if(type(STK(arg1)) == OT_INTEGER) {
+                                       SQInteger t = _integer(STK(arg1));
+                                       TARGET = SQInteger(~t);
+                                       continue;
+                               }
+                               Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1)));
+                               SQ_THROW();
+                       case _OP_CLOSURE: {
+                               SQClosure *c = ci->_closure._unVal.pClosure;
+                               SQFunctionProto *fp = c->_function._unVal.pFunctionProto;
+                               if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); }
+                               continue;
+                       }
+                       case _OP_YIELD:{
+                               if(type(ci->_generator) == OT_GENERATOR) {
+                                       if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);
+                                       _GUARD(_generator(ci->_generator)->Yield(this));
+                                       traps -= ci->_etraps;
+                                       if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;
+                               }
+                               else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();}
+                               if(Return(arg0, arg1, temp_reg)){
+                                       assert(traps == 0);
+                                       outres = temp_reg;
+                                       return true;
+                               }
+                                       
+                               }
+                               continue;
+                       case _OP_RESUME:
+                               if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();}
+                               _GUARD(_generator(STK(arg1))->Resume(this, arg0));
+                               traps += ci->_etraps;
+                continue;
+                       case _OP_FOREACH:{ int tojump;
+                               _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,sarg1,tojump));
+                               ci->_ip += tojump; }
+                               continue;
+                       case _OP_POSTFOREACH:
+                               assert(type(STK(arg0)) == OT_GENERATOR);
+                               if(_generator(STK(arg0))->_state == SQGenerator::eDead) 
+                                       ci->_ip += (sarg1 - 1);
+                               continue;
+                       case _OP_DELEGATE: _GUARD(DELEGATE_OP(TARGET,STK(arg1),STK(arg2))); continue;
+                       case _OP_CLONE:
+                               if(!Clone(STK(arg1), TARGET))
+                               { Raise_Error(_SC("cloning a %s"), GetTypeName(STK(arg1))); SQ_THROW();}
+                               continue;
+                       case _OP_TYPEOF: TypeOf(STK(arg1), TARGET); continue;
+                       case _OP_PUSHTRAP:{
+                               SQInstruction *_iv = _funcproto(_closure(ci->_closure)->_function)->_instructions;
+                               _etraps.push_back(SQExceptionTrap(_top,_stackbase, &_iv[(ci->_ip-_iv)+arg1], arg0)); traps++;
+                               ci->_etraps++;
+                                                         }
+                               continue;
+                       case _OP_POPTRAP: {
+                               for(SQInteger i = 0; i < arg0; i++) {
+                                       _etraps.pop_back(); traps--;
+                                       ci->_etraps--;
+                               }
+                                                         }
+                               continue;
+                       case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue;
+                       case _OP_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue;
+                       case _OP_NEWSLOTA:
+                               bool bstatic = (arg0&NEW_SLOT_STATIC_FLAG)?true:false;
+                               if(type(STK(arg1)) == OT_CLASS) {
+                                       if(type(_class(STK(arg1))->_metamethods[MT_NEWMEMBER]) != OT_NULL ) {
+                                               Push(STK(arg1)); Push(STK(arg2)); Push(STK(arg3));
+                                               Push((arg0&NEW_SLOT_ATTRIBUTES_FLAG) ? STK(arg2-1) : _null_);
+                                               int nparams = 4;
+                                               if(Call(_class(STK(arg1))->_metamethods[MT_NEWMEMBER], nparams, _top - nparams, temp_reg,SQFalse)) {
+                                                       Pop(nparams);
+                                                       continue;
+                                               }
+                                       }
+                               }
+                               _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),bstatic));
+                               if((arg0&NEW_SLOT_ATTRIBUTES_FLAG)) {
+                                       _class(STK(arg1))->SetAttributes(STK(arg2),STK(arg2-1));
+                               }
+                               continue;
+                       }
+                       
+               }
+       }
+exception_trap:
+       {
+               SQObjectPtr currerror = _lasterror;
+//             dumpstack(_stackbase);
+               SQInteger n = 0;
+               SQInteger last_top = _top;
+               if(ci) {
+                       if(_ss(this)->_notifyallexceptions) CallErrorHandler(currerror);
+
+                       if(traps) {
+                               do {
+                                       if(ci->_etraps > 0) {
+                                               SQExceptionTrap &et = _etraps.top();
+                                               ci->_ip = et._ip;
+                                               _top = et._stacksize;
+                                               _stackbase = et._stackbase;
+                                               _stack._vals[_stackbase+et._extarget] = currerror;
+                                               _etraps.pop_back(); traps--; ci->_etraps--;
+                                               while(last_top >= _top) _stack._vals[last_top--].Null();
+                                               goto exception_restore;
+                                       }
+                                       //if is a native closure
+                                       if(type(ci->_closure) != OT_CLOSURE && n)
+                                               break;
+                                       if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+                                       PopVarArgs(ci->_vargs);
+                                       POP_CALLINFO(this);
+                                       n++;
+                               } while(_callsstacksize);
+                       }
+                       else {
+                               //call the hook
+                               if(raiseerror && !_ss(this)->_notifyallexceptions)
+                                       CallErrorHandler(currerror);
+                       }
+                       //remove call stack until a C function is found or the cstack is empty
+                       if(ci) do {
+                               SQBool exitafterthisone = ci->_root;
+                               if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+                               _stackbase -= ci->_prevstkbase;
+                               _top = _stackbase + ci->_prevtop;
+                               PopVarArgs(ci->_vargs);
+                               POP_CALLINFO(this);
+                               if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;
+                       } while(_callsstacksize);
+
+                       while(last_top >= _top) _stack._vals[last_top--].Null();
+               }
+               _lasterror = currerror;
+               return false;
+       }
+       assert(0);
+}
+
+bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor)
+{
+       inst = theclass->CreateInstance();
+       if(!theclass->Get(_ss(this)->_constructoridx,constructor)) {
+               //if(!Call(constr,nargs,stackbase,constr,false))
+               //      return false;
+               constructor = _null_;
+       }
+       return true;
+}
+
+void SQVM::CallErrorHandler(SQObjectPtr &error)
+{
+       if(type(_errorhandler) != OT_NULL) {
+               SQObjectPtr out;
+               Push(_roottable); Push(error);
+               Call(_errorhandler, 2, _top-2, out,SQFalse);
+               Pop(2);
+       }
+}
+
+void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline)
+{
+       SQObjectPtr temp_reg;
+       SQInteger nparams=5;
+       SQFunctionProto *func=_funcproto(_closure(ci->_closure)->_function);
+       Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name);
+       Call(_debughook,nparams,_top-nparams,temp_reg,SQFalse);
+       Pop(nparams);
+}
+
+bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackbase,SQObjectPtr &retval,bool &suspend)
+{
+       if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+       SQInteger nparamscheck = nclosure->_nparamscheck;
+       if(((nparamscheck > 0) && (nparamscheck != nargs))
+               || ((nparamscheck < 0) && (nargs < (-nparamscheck)))) {
+               Raise_Error(_SC("wrong number of parameters"));
+               return false;
+               }
+
+       SQInteger tcs;
+       if((tcs = nclosure->_typecheck.size())) {
+               for(SQInteger i = 0; i < nargs && i < tcs; i++)
+                       if((nclosure->_typecheck._vals[i] != -1) && !(type(_stack._vals[stackbase+i]) & nclosure->_typecheck[i])) {
+                Raise_ParamTypeError(i,nclosure->_typecheck._vals[i],type(_stack._vals[stackbase+i]));
+                               return false;
+                       }
+       }
+       _nnativecalls++;
+       if ((_top + MIN_STACK_OVERHEAD) > (SQInteger)_stack.size()) {
+               _stack.resize(_stack.size() + (MIN_STACK_OVERHEAD<<1));
+       }
+       SQInteger oldtop = _top;
+       SQInteger oldstackbase = _stackbase;
+       _top = stackbase + nargs;
+       CallInfo lci;
+       lci._etraps = 0;
+       lci._closure._unVal.pNativeClosure = nclosure;
+       lci._closure._type = OT_NATIVECLOSURE;
+       lci._prevstkbase = (SQInt32) (stackbase - _stackbase);
+       lci._ncalls = 1;
+       lci._prevtop = (SQInt32) (oldtop - oldstackbase);
+       PUSH_CALLINFO(this, lci);
+       _stackbase = stackbase;
+       //push free variables
+       SQInteger outers = nclosure->_outervalues.size();
+       for (SQInteger i = 0; i < outers; i++) {
+               Push(nclosure->_outervalues[i]);
+       }
+
+       if(type(nclosure->_env) == OT_WEAKREF) {
+               _stack[stackbase] = _weakref(nclosure->_env)->_obj;
+       }
+
+       
+       SQInteger ret = (nclosure->_function)(this);
+       _nnativecalls--;
+       suspend = false;
+       if( ret == SQ_SUSPEND_FLAG) suspend = true;
+       else if (ret < 0) { 
+               _stackbase = oldstackbase;
+               _top = oldtop;
+               POP_CALLINFO(this);
+               Raise_Error(_lasterror);
+               return false;
+       }
+       
+       if (ret != 0){ retval = TOP(); }
+       else { retval = _null_; }
+       _stackbase = oldstackbase;
+       _top = oldtop;
+       POP_CALLINFO(this);
+       return true;
+}
+
+bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw, bool fetchroot)
+{
+       switch(type(self)){
+       case OT_TABLE:
+               if(_table(self)->Get(key,dest))return true;
+               break;
+       case OT_ARRAY:
+               if(sq_isnumeric(key)){
+                       return _array(self)->Get(tointeger(key),dest);
+               }
+               break;
+       case OT_INSTANCE:
+               if(_instance(self)->Get(key,dest)) return true;
+               break;
+       default:break; //shut up compiler
+       }
+       if(FallBackGet(self,key,dest,raw)) return true;
+
+       if(fetchroot) {
+               if(_rawval(STK(0)) == _rawval(self) &&
+                       type(STK(0)) == type(self)) {
+                               return _table(_roottable)->Get(key,dest);
+               }
+       }
+       return false;
+}
+
+bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw)
+{
+       switch(type(self)){
+       case OT_CLASS: 
+               return _class(self)->Get(key,dest);
+               break;
+       case OT_TABLE:
+       case OT_USERDATA:
+        //delegation
+               if(_delegable(self)->_delegate) {
+                       if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,raw,false))
+                               return true;    
+                       if(raw)return false;
+                       Push(self);Push(key);
+                       if(CallMetaMethod(_delegable(self),MT_GET,2,dest))
+                               return true;
+               }
+               if(type(self) == OT_TABLE) {
+                       if(raw) return false;
+                       return _table_ddel->Get(key,dest);
+               }
+               return false;
+               break;
+       case OT_ARRAY:
+               if(raw)return false;
+               return _array_ddel->Get(key,dest);
+       case OT_STRING:
+               if(sq_isnumeric(key)){
+                       SQInteger n=tointeger(key);
+                       if(abs((int)n)<_string(self)->_len){
+                               if(n<0)n=_string(self)->_len-n;
+                               dest=SQInteger(_stringval(self)[n]);
+                               return true;
+                       }
+                       return false;
+               }
+               else {
+                       if(raw)return false;
+                       return _string_ddel->Get(key,dest);
+               }
+               break;
+       case OT_INSTANCE:
+               if(raw)return false;
+               Push(self);Push(key);
+               if(!CallMetaMethod(_delegable(self),MT_GET,2,dest)) {
+                       return _instance_ddel->Get(key,dest);
+               }
+               return true;
+       case OT_INTEGER:case OT_FLOAT:case OT_BOOL: 
+               if(raw)return false;
+               return _number_ddel->Get(key,dest);
+       case OT_GENERATOR: 
+               if(raw)return false;
+               return _generator_ddel->Get(key,dest);
+       case OT_CLOSURE: case OT_NATIVECLOSURE: 
+               if(raw)return false;
+               return _closure_ddel->Get(key,dest);
+       case OT_THREAD:
+               if(raw)return false;
+               return  _thread_ddel->Get(key,dest);
+       case OT_WEAKREF:
+               if(raw)return false;
+               return  _weakref_ddel->Get(key,dest);
+       default:return false;
+       }
+       return false;
+}
+
+bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool fetchroot)
+{
+       switch(type(self)){
+       case OT_TABLE:
+               if(_table(self)->Set(key,val))
+                       return true;
+               if(_table(self)->_delegate) {
+                       if(Set(_table(self)->_delegate,key,val,false)) {
+                               return true;
+                       }
+               }
+               //keeps going
+       case OT_USERDATA:
+               if(_delegable(self)->_delegate) {
+                       SQObjectPtr t;
+                       Push(self);Push(key);Push(val);
+                       if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+               }
+               break;
+       case OT_INSTANCE:{
+               if(_instance(self)->Set(key,val))
+                       return true;
+               SQObjectPtr t;
+               Push(self);Push(key);Push(val);
+               if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+               }
+               break;
+       case OT_ARRAY:
+               if(!sq_isnumeric(key)) {Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; }
+               return _array(self)->Set(tointeger(key),val);
+       default:
+               Raise_Error(_SC("trying to set '%s'"),GetTypeName(self));
+               return false;
+       }
+       if(fetchroot) {
+               if(_rawval(STK(0)) == _rawval(self) &&
+                       type(STK(0)) == type(self)) {
+                               return _table(_roottable)->Set(key,val);
+                       }
+       }
+       return false;
+}
+
+bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target)
+{
+       SQObjectPtr temp_reg;
+       SQObjectPtr newobj;
+       switch(type(self)){
+       case OT_TABLE:
+               newobj = _table(self)->Clone();
+               goto cloned_mt;
+       case OT_INSTANCE:
+               newobj = _instance(self)->Clone(_ss(this));
+cloned_mt:
+               if(_delegable(newobj)->_delegate){
+                       Push(newobj);
+                       Push(self);
+                       CallMetaMethod(_delegable(newobj),MT_CLONED,2,temp_reg);
+               }
+               target = newobj;
+               return true;
+       case OT_ARRAY: 
+               target = _array(self)->Clone();
+               return true;
+       default: return false;
+       }
+}
+
+bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
+{
+       if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; }
+       switch(type(self)) {
+       case OT_TABLE: {
+               bool rawcall = true;
+               if(_table(self)->_delegate) {
+                       SQObjectPtr res;
+                       if(!_table(self)->Get(key,res)) {
+                               Push(self);Push(key);Push(val);
+                               rawcall = !CallMetaMethod(_table(self),MT_NEWSLOT,3,res);
+                       }
+               }
+               if(rawcall) _table(self)->NewSlot(key,val); //cannot fail
+               
+               break;}
+       case OT_CLASS: 
+               if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) {
+                       if(_class(self)->_locked) {
+                               Raise_Error(_SC("trying to modify a class that has already been instantiated"));
+                               return false;
+                       }
+                       else {
+                               SQObjectPtr oval = PrintObjVal(key);
+                               Raise_Error(_SC("the property '%s' already exists"),_stringval(oval));
+                               return false;
+                       }
+               }
+               break;
+       default:
+               Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key));
+               return false;
+               break;
+       }
+       return true;
+}
+
+bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res)
+{
+       switch(type(self)) {
+       case OT_TABLE:
+       case OT_INSTANCE:
+       case OT_USERDATA: {
+               SQObjectPtr t;
+               bool handled = false;
+               if(_delegable(self)->_delegate) {
+                       Push(self);Push(key);
+                       handled = CallMetaMethod(_delegable(self),MT_DELSLOT,2,t);
+               }
+
+               if(!handled) {
+                       if(type(self) == OT_TABLE) {
+                               if(_table(self)->Get(key,t)) {
+                                       _table(self)->Remove(key);
+                               }
+                               else {
+                                       Raise_IdxError((SQObject &)key);
+                                       return false;
+                               }
+                       }
+                       else {
+                               Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self));
+                               return false;
+                       }
+               }
+               res = t;
+                               }
+               break;
+       default:
+               Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self));
+               return false;
+       }
+       return true;
+}
+
+bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror)
+{
+#ifdef _DEBUG
+SQInteger prevstackbase = _stackbase;
+#endif
+       switch(type(closure)) {
+       case OT_CLOSURE:
+               return Execute(closure, _top - nparams, nparams, stackbase,outres,raiseerror);
+               break;
+       case OT_NATIVECLOSURE:{
+               bool suspend;
+               return CallNative(_nativeclosure(closure), nparams, stackbase, outres,suspend);
+               
+                                                 }
+               break;
+       case OT_CLASS: {
+               SQObjectPtr constr;
+               SQObjectPtr temp;
+               CreateClassInstance(_class(closure),outres,constr);
+               if(type(constr) != OT_NULL) {
+                       _stack[stackbase] = outres;
+                       return Call(constr,nparams,stackbase,temp,raiseerror);
+               }
+               return true;
+                                  }
+               break;
+       default:
+               return false;
+       }
+#ifdef _DEBUG
+       if(!_suspended) {
+               assert(_stackbase == prevstackbase);
+       }
+#endif
+       return true;
+}
+
+bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,SQInteger nparams,SQObjectPtr &outres)
+{
+       SQObjectPtr closure;
+       if(del->GetMetaMethod(this, mm, closure)) {
+               if(Call(closure, nparams, _top - nparams, outres, SQFalse)) {
+                       Pop(nparams);
+                       return true;
+               }
+       }
+       Pop(nparams);
+       return false;
+}
+
+void SQVM::Remove(SQInteger n) {
+       n = (n >= 0)?n + _stackbase - 1:_top + n;
+       for(SQInteger i = n; i < _top; i++){
+               _stack[i] = _stack[i+1];
+       }
+       _stack[_top] = _null_;
+       _top--;
+}
+
+void SQVM::Pop() {
+       _stack[--_top] = _null_;
+}
+
+void SQVM::Pop(SQInteger n) {
+       for(SQInteger i = 0; i < n; i++){
+               _stack[--_top] = _null_;
+       }
+}
+
+void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; }
+SQObjectPtr &SQVM::Top() { return _stack[_top-1]; }
+SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; }
+SQObjectPtr &SQVM::GetUp(SQInteger n) { return _stack[_top+n]; }
+SQObjectPtr &SQVM::GetAt(SQInteger n) { return _stack[n]; }
+
+#ifdef _DEBUG_DUMP
+void SQVM::dumpstack(SQInteger stackbase,bool dumpall)
+{
+       SQInteger size=dumpall?_stack.size():_top;
+       SQInteger n=0;
+       scprintf(_SC("\n>>>>stack dump<<<<\n"));
+       CallInfo &ci=_callsstack[_callsstacksize-1];
+       scprintf(_SC("IP: %p\n"),ci._ip);
+       scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase);
+       scprintf(_SC("prev top: %d\n"),ci._prevtop);
+       for(SQInteger i=0;i<size;i++){
+               SQObjectPtr &obj=_stack[i];     
+               if(stackbase==i)scprintf(_SC(">"));else scprintf(_SC(" "));
+               scprintf(_SC("[%d]:"),n);
+               switch(type(obj)){
+               case OT_FLOAT:                  scprintf(_SC("FLOAT %.3f"),_float(obj));break;
+               case OT_INTEGER:                scprintf(_SC("INTEGER %d"),_integer(obj));break;
+               case OT_BOOL:                   scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break;
+               case OT_STRING:                 scprintf(_SC("STRING %s"),_stringval(obj));break;
+               case OT_NULL:                   scprintf(_SC("NULL"));  break;
+               case OT_TABLE:                  scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break;
+               case OT_ARRAY:                  scprintf(_SC("ARRAY %p"),_array(obj));break;
+               case OT_CLOSURE:                scprintf(_SC("CLOSURE [%p]"),_closure(obj));break;
+               case OT_NATIVECLOSURE:  scprintf(_SC("NATIVECLOSURE"));break;
+               case OT_USERDATA:               scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break;
+               case OT_GENERATOR:              scprintf(_SC("GENERATOR"));break;
+               case OT_THREAD:                 scprintf(_SC("THREAD [%p]"),_thread(obj));break;
+               case OT_USERPOINTER:    scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break;
+               case OT_CLASS:                  scprintf(_SC("CLASS %p"),_class(obj));break;
+               case OT_INSTANCE:               scprintf(_SC("INSTANCE %p"),_instance(obj));break;
+               case OT_WEAKREF:                scprintf(_SC("WEAKERF %p"),_weakref(obj));break;
+               default:
+                       assert(0);
+                       break;
+               };
+               scprintf(_SC("\n"));
+               ++n;
+       }
+}
+
+
+
+#endif
diff --git a/src/squirrel/squirrel/sqvm.h b/src/squirrel/squirrel/sqvm.h
new file mode 100644 (file)
index 0000000..89710ff
--- /dev/null
@@ -0,0 +1,203 @@
+/*     see copyright notice in squirrel.h */
+#ifndef _SQVM_H_
+#define _SQVM_H_
+
+#include "sqopcodes.h"
+#include "sqobject.h"
+#define MAX_NATIVE_CALLS 100
+#define MIN_STACK_OVERHEAD 10
+
+#define SQ_SUSPEND_FLAG -666
+//base lib
+void sq_base_register(HSQUIRRELVM v);
+
+struct SQExceptionTrap{
+       SQExceptionTrap() {}
+       SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;}
+       SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et;      }
+       SQInteger _stackbase;
+       SQInteger _stacksize;
+       SQInstruction *_ip;
+       SQInteger _extarget;
+};
+
+#define _INLINE 
+
+#define STK(a) _stack._vals[_stackbase+(a)]
+#define TARGET _stack._vals[_stackbase+arg0]
+
+typedef sqvector<SQExceptionTrap> ExceptionsTraps;
+
+struct SQVM : public CHAINABLE_OBJ
+{
+       struct VarArgs {
+               VarArgs() { size = 0; base = 0; }
+               unsigned short size;
+               unsigned short base;
+       };
+
+       struct CallInfo{
+               CallInfo() { _generator._type = OT_NULL;}
+               SQInstruction *_ip;
+               SQObjectPtr *_literals;
+               SQObject _closure;
+               SQObject _generator;
+               SQInt32 _etraps;
+               SQInt32 _prevstkbase;
+               SQInt32 _prevtop;
+               SQInt32 _target;
+               SQInt32 _ncalls;
+               SQBool _root;
+               VarArgs _vargs;
+       };
+       
+typedef sqvector<CallInfo> CallInfoVec;
+public:
+       enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM };
+       SQVM(SQSharedState *ss);
+       ~SQVM();
+       bool Init(SQVM *friendvm, SQInteger stacksize);
+       bool Execute(SQObjectPtr &func, SQInteger target, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL);
+       //starts a native call return when the NATIVE closure returns
+       bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger stackbase, SQObjectPtr &retval,bool &suspend);
+       //starts a SQUIRREL call in the same "Execution loop"
+       bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall);
+       bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor);
+       //call a generic closure pure SQUIRREL or NATIVE
+       bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror);
+       SQRESULT Suspend();
+
+       void CallDebugHook(SQInteger type,SQInteger forcedline=0);
+       void CallErrorHandler(SQObjectPtr &e);
+       bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, bool raw, bool fetchroot);
+       bool FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw);
+       bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, bool fetchroot);
+       bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic);
+       bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res);
+       bool Clone(const SQObjectPtr &self, SQObjectPtr &target);
+       bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res);
+       bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest);
+       bool IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res);
+       void ToString(const SQObjectPtr &o,SQObjectPtr &res);
+       SQString *PrintObjVal(const SQObject &o);
+
+       void Raise_Error(const SQChar *s, ...);
+       void Raise_Error(SQObjectPtr &desc);
+       void Raise_IdxError(SQObject &o);
+       void Raise_CompareError(const SQObject &o1, const SQObject &o2);
+       void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type);
+
+       void TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest);
+       bool CallMetaMethod(SQDelegable *del, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres);
+       bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest);
+       bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval);
+       //new stuff
+       _INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
+       _INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
+       _INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1);
+       _INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res);
+       bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func);
+       bool GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &idx,CallInfo *ci);
+       bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs);
+       bool GETPARENT_OP(SQObjectPtr &o,SQObjectPtr &target);
+       //return true if the loop is finished
+       bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump);
+       bool DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2);
+       _INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
+       _INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
+       _INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix);
+       void PopVarArgs(VarArgs &vargs);
+#ifdef _DEBUG_DUMP
+       void dumpstack(SQInteger stackbase=-1, bool dumpall = false);
+#endif
+
+#ifndef NO_GARBAGE_COLLECTOR
+       void Mark(SQCollectable **chain);
+#endif
+       void Finalize();
+       void GrowCallStack() {
+               SQInteger newsize = _alloccallsstacksize*2;
+               _callsstack = (CallInfo*)sq_realloc(_callsstack,_alloccallsstacksize*sizeof(CallInfo),newsize*sizeof(CallInfo));
+               _alloccallsstacksize = newsize;
+       }
+       void Release(){ sq_delete(this,SQVM); } //does nothing
+////////////////////////////////////////////////////////////////////////////
+       //stack functions for the api
+       void Remove(SQInteger n);
+
+       bool IsFalse(SQObjectPtr &o);
+       
+       void Pop();
+       void Pop(SQInteger n);
+       void Push(const SQObjectPtr &o);
+       SQObjectPtr &Top();
+       SQObjectPtr &PopGet();
+       SQObjectPtr &GetUp(SQInteger n);
+       SQObjectPtr &GetAt(SQInteger n);
+
+       SQObjectPtrVec _stack;
+       SQObjectPtrVec _vargsstack;
+       SQInteger _top;
+       SQInteger _stackbase;
+       SQObjectPtr _roottable;
+       SQObjectPtr _lasterror;
+       SQObjectPtr _errorhandler;
+       SQObjectPtr _debughook;
+
+       SQObjectPtr temp_reg;
+       
+
+       CallInfo* _callsstack;
+       SQInteger _callsstacksize;
+       SQInteger _alloccallsstacksize;
+
+       ExceptionsTraps _etraps;
+       CallInfo *ci;
+       void *_foreignptr;
+       //VMs sharing the same state
+       SQSharedState *_sharedstate;
+       SQInteger _nnativecalls;
+       //suspend infos
+       SQBool _suspended;
+       SQBool _suspended_root;
+       SQInteger _suspended_target;
+       SQInteger _suspended_traps;
+       VarArgs _suspend_varargs;
+};
+
+struct AutoDec{
+       AutoDec(SQInteger *n) { _n = n; }
+       ~AutoDec() { (*_n)--; }
+       SQInteger *_n;
+};
+
+inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}
+const SQChar *GetTypeName(const SQObjectPtr &obj1);
+const SQChar *IdType2Name(SQObjectType type);
+
+#define _ss(_vm_) (_vm_)->_sharedstate
+
+#ifndef NO_GARBAGE_COLLECTOR
+#define _opt_ss(_vm_) (_vm_)->_sharedstate
+#else
+#define _opt_ss(_vm_) NULL
+#endif
+
+#define PUSH_CALLINFO(v,nci){ \
+       if(v->_callsstacksize == v->_alloccallsstacksize) { \
+               v->GrowCallStack(); \
+       } \
+       v->ci = &v->_callsstack[v->_callsstacksize]; \
+       *(v->ci) = nci; \
+       v->_callsstacksize++; \
+}
+
+#define POP_CALLINFO(v){ \
+       v->_callsstacksize--; \
+       if(v->_callsstacksize)  \
+               v->ci = &v->_callsstack[v->_callsstacksize-1] ; \
+       else    \
+               v->ci = NULL; \
+}
+#endif //_SQVM_H_
diff --git a/src/statistics.cpp b/src/statistics.cpp
new file mode 100644 (file)
index 0000000..5eb0cef
--- /dev/null
@@ -0,0 +1,355 @@
+//  $Id$
+//
+//  SuperTux (Statistics module)
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//  Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#include <config.h>
+
+#include <assert.h>
+#include <math.h>
+#include <sstream>
+#include <limits>
+#include "video/drawing_context.hpp"
+#include "gettext.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "statistics.hpp"
+#include "log.hpp"
+#include "scripting/squirrel_util.hpp"
+
+namespace {
+  const int nv_coins = std::numeric_limits<int>::min();
+  const int nv_badguys = std::numeric_limits<int>::min();
+  const float nv_time = std::numeric_limits<float>::max();
+  const int nv_secrets = std::numeric_limits<int>::min();
+}
+
+float WMAP_INFO_LEFT_X;
+float WMAP_INFO_RIGHT_X;
+float WMAP_INFO_TOP_Y1;
+float WMAP_INFO_TOP_Y2;
+
+Statistics::Statistics() : coins(nv_coins), total_coins(nv_coins), badguys(nv_badguys), total_badguys(nv_badguys), time(nv_time), secrets(nv_secrets), total_secrets(nv_secrets), valid(true), display_stat(0)
+{
+  WMAP_INFO_LEFT_X = (SCREEN_WIDTH/2 + 80) + 32;
+  WMAP_INFO_RIGHT_X = SCREEN_WIDTH/2 + 368;
+  WMAP_INFO_TOP_Y1 = SCREEN_HEIGHT/2 + 172 - 16;
+  WMAP_INFO_TOP_Y2 = SCREEN_HEIGHT/2 + 172;
+}
+
+Statistics::~Statistics()
+{
+}
+
+/*
+void
+Statistics::parse(const lisp::Lisp& reader)
+{
+  reader.get("coins-collected", coins);
+  reader.get("coins-collected-total", total_coins);
+  reader.get("badguys-killed", badguys);
+  reader.get("badguys-killed-total", total_badguys);
+  reader.get("time-needed", time);
+  reader.get("secrets-found", secrets);
+  reader.get("secrets-found-total", total_secrets);
+}
+
+void
+Statistics::write(lisp::Writer& writer)
+{
+  writer.write_int("coins-collected", coins);
+  writer.write_int("coins-collected-total", total_coins);
+  writer.write_int("badguys-killed", badguys);
+  writer.write_int("badguys-killed-total", total_badguys);
+  writer.write_float("time-needed", time);
+  writer.write_int("secrets-found", secrets);
+  writer.write_int("secrets-found-total", total_secrets);
+}
+*/
+
+void
+Statistics::serialize_to_squirrel(HSQUIRRELVM vm)
+{
+  // TODO: there's some bug in the unserialization routines that breaks stuff when an empty statistics table is written, so -- as a workaround -- let's make sure we will actually write something first
+  if (!((coins != nv_coins) || (total_coins != nv_coins) || (badguys != nv_badguys) || (total_badguys != nv_badguys) || (time != nv_time) || (secrets != nv_secrets) || (total_secrets != nv_secrets))) return;
+
+  sq_pushstring(vm, "statistics", -1);
+  sq_newtable(vm);
+  if (coins != nv_coins) Scripting::store_int(vm, "coins-collected", coins);
+  if (total_coins != nv_coins) Scripting::store_int(vm, "coins-collected-total", total_coins);
+  if (badguys != nv_badguys) Scripting::store_int(vm, "badguys-killed", badguys);
+  if (total_badguys != nv_badguys) Scripting::store_int(vm, "badguys-killed-total", total_badguys);
+  if (time != nv_time) Scripting::store_float(vm, "time-needed", time);
+  if (secrets != nv_secrets) Scripting::store_int(vm, "secrets-found", secrets);
+  if (total_secrets != nv_secrets) Scripting::store_int(vm, "secrets-found-total", total_secrets);
+  sq_createslot(vm, -3);
+}
+
+void
+Statistics::unserialize_from_squirrel(HSQUIRRELVM vm)
+{
+  sq_pushstring(vm, "statistics", -1);
+  if(SQ_FAILED(sq_get(vm, -2))) {
+    return;
+  }
+  Scripting::get_int(vm, "coins-collected", coins);
+  Scripting::get_int(vm, "coins-collected-total", total_coins);
+  Scripting::get_int(vm, "badguys-killed", badguys);
+  Scripting::get_int(vm, "badguys-killed-total", total_badguys);
+  Scripting::get_float(vm, "time-needed", time);
+  Scripting::get_int(vm, "secrets-found", secrets);
+  Scripting::get_int(vm, "secrets-found-total", total_secrets);
+  sq_pop(vm, 1);
+}
+
+//define TOTAL_DISPLAY_TIME  3400
+//define FADING_TIME          600
+
+#define TOTAL_DISPLAY_TIME  5
+#define FADING_TIME         1
+
+void
+Statistics::draw_worldmap_info(DrawingContext& context)
+{
+  // skip draw if level was never played
+  if (coins == nv_coins) return;
+
+  // skip draw if stats were declared invalid
+  if (!valid) return;
+
+  context.draw_text(white_small_text, std::string("- ") + _("Best Level Statistics") + " -", Vector((WMAP_INFO_LEFT_X + WMAP_INFO_RIGHT_X) / 2, WMAP_INFO_TOP_Y1), ALIGN_CENTER, LAYER_GUI);
+
+  float alpha;
+  if(timer.get_timegone() < FADING_TIME)
+    alpha = (timer.get_timegone() * 1.0f / FADING_TIME);
+  else if(timer.get_timeleft() < FADING_TIME)
+    alpha = (timer.get_timeleft() * 1.0f / FADING_TIME);
+  else
+    alpha = 1.0f;
+
+  context.push_transform();
+  context.set_alpha(alpha);
+
+  char caption_buf[128];
+  char stat_buf[128];
+  switch (display_stat)
+  {
+    case 0:
+      snprintf(caption_buf, sizeof(caption_buf), _("Max coins collected:"));
+      snprintf(stat_buf, sizeof(stat_buf), "%d/%d", coins, total_coins);
+      break;
+    case 1:
+      snprintf(caption_buf, sizeof(caption_buf), _("Max fragging:"));
+      snprintf(stat_buf, sizeof(stat_buf), "%d/%d", badguys, total_badguys);
+      break;
+    case 2:
+      snprintf(caption_buf, sizeof(caption_buf), _("Min time needed:"));
+      {
+       int csecs = (int)(time * 100);
+       int mins = (int)(csecs / 6000);
+       int secs = (csecs % 6000) / 100;
+       snprintf(stat_buf, sizeof(stat_buf), "%02d:%02d", mins,secs);
+      }
+      break;
+    case 3:
+      snprintf(caption_buf, sizeof(caption_buf), _("Max secrets found:"));
+      snprintf(stat_buf, sizeof(stat_buf), "%d/%d", secrets, total_secrets);
+      break;
+    default:
+      log_debug << "Invalid stat requested to be drawn" << std::endl;
+      break;
+  }
+
+  if (!timer.started())
+  {
+    timer.start(TOTAL_DISPLAY_TIME);
+    display_stat++;
+    if (display_stat > 3) display_stat = 0;
+  }
+
+  context.draw_text(white_small_text, caption_buf, Vector(WMAP_INFO_LEFT_X, WMAP_INFO_TOP_Y2), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_small_text, stat_buf, Vector(WMAP_INFO_RIGHT_X, WMAP_INFO_TOP_Y2), ALIGN_RIGHT, LAYER_GUI);
+  context.pop_transform();
+}
+
+void
+Statistics::draw_message_info(DrawingContext& context, std::string title)
+{
+  // skip draw if level was never played
+  // TODO: do we need this?
+  if (coins == nv_coins) return;
+
+  // skip draw if stats were declared invalid
+  if (!valid) return;
+
+  const float width = white_small_text->get_text_width("Max coins collected: 1111 / 1111");
+  const float left = (SCREEN_WIDTH - width) / 2;
+  const float right = (SCREEN_WIDTH + width) / 2;
+
+  context.draw_text(gold_text, title, Vector(SCREEN_WIDTH/2, 410), ALIGN_CENTER, LAYER_GUI);
+
+  char stat_buf[128];
+  int py = 450 + 18;
+
+  snprintf(stat_buf, sizeof(stat_buf), "%d/%d", coins, total_coins);
+  context.draw_text(white_small_text, _("Max coins collected:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+  py+=18;
+
+  snprintf(stat_buf, sizeof(stat_buf), "%d/%d", badguys, total_badguys);
+  context.draw_text(white_small_text, _("Max fragging:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+  py+=18;
+
+  int csecs = (int)(time * 100);
+  int mins = (int)(csecs / 6000);
+  int secs = (csecs % 6000) / 100;
+  snprintf(stat_buf, sizeof(stat_buf), "%02d:%02d", mins,secs);
+  context.draw_text(white_small_text, _("Min time needed:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_small_text, "%02d:%02d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+  py+=18;
+
+  snprintf(stat_buf, sizeof(stat_buf), "%d/%d", secrets, total_secrets);
+  context.draw_text(white_small_text, _("Max secrets found:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+  py+=18;
+}
+
+void
+Statistics::draw_endseq_panel(DrawingContext& context, Statistics* best_stats, Surface* backdrop)
+{
+  // skip draw if level was never played
+  // TODO: do we need this?
+  if (coins == nv_coins) return;
+
+  // skip draw if stats were declared invalid
+  if (!valid) return;
+
+  // abort if we have no backdrop
+  if (!backdrop) return;
+
+  int box_w = 220+110+110;
+  int box_h = 30+20+20+20;
+  int box_x = (int)((SCREEN_WIDTH - box_w) / 2);
+  int box_y = (int)(SCREEN_HEIGHT / 2) - box_h;
+
+  int bd_w = (int)backdrop->get_width();
+  int bd_h = (int)backdrop->get_height();
+  int bd_x = (int)((SCREEN_WIDTH - bd_w) / 2);
+  int bd_y = box_y + (box_h / 2) - (bd_h / 2);
+
+  int col1_x = box_x;
+  int col2_x = col1_x+200;
+  int col3_x = col2_x+130;
+
+  int row1_y = box_y;
+  int row2_y = row1_y+30;
+  int row3_y = row2_y+20;
+  int row4_y = row3_y+20;
+
+  context.push_transform();
+  context.set_alpha(0.5);
+  context.draw_surface(backdrop, Vector(bd_x, bd_y), LAYER_GUI);
+  context.pop_transform();
+
+  char buf[129];
+  context.draw_text(white_text, _("You"), Vector(col2_x, row1_y), ALIGN_LEFT, LAYER_GUI);
+  context.draw_text(white_text, _("Best"), Vector(col3_x, row1_y), ALIGN_LEFT, LAYER_GUI);
+
+  context.draw_text(white_text, _("Coins"), Vector(col2_x-16, row2_y), ALIGN_RIGHT, LAYER_GUI);
+  snprintf(buf, sizeof(buf), "%d/%d", std::min(coins, 999), std::min(total_coins, 999));
+  context.draw_text(gold_text, buf, Vector(col2_x, row2_y), ALIGN_LEFT, LAYER_GUI);
+  if (best_stats && (best_stats->coins > coins)) {
+    snprintf(buf, sizeof(buf), "%d/%d", std::min(best_stats->coins, 999), std::min(best_stats->total_coins, 999));
+  }
+  context.draw_text(gold_text, buf, Vector(col3_x, row2_y), ALIGN_LEFT, LAYER_GUI);
+
+  context.draw_text(white_text, _("Secrets"), Vector(col2_x-16, row4_y), ALIGN_RIGHT, LAYER_GUI);
+  snprintf(buf, sizeof(buf), "%d/%d", secrets, total_secrets);
+  context.draw_text(gold_text, buf, Vector(col2_x, row4_y), ALIGN_LEFT, LAYER_GUI);
+  if (best_stats && (best_stats->secrets > secrets)) {
+    snprintf(buf, sizeof(buf), "%d/%d", best_stats->secrets, best_stats->total_secrets);
+  }
+  context.draw_text(gold_text, buf, Vector(col3_x, row4_y), ALIGN_LEFT, LAYER_GUI);
+
+  context.draw_text(white_text, _("Time"), Vector(col2_x-16, row3_y), ALIGN_RIGHT, LAYER_GUI);
+  int csecs = (int)(time * 100);
+  int mins = (int)(csecs / 6000);
+  int secs = (csecs % 6000) / 100;
+  snprintf(buf, sizeof(buf), "%02d:%02d", mins,secs);
+  context.draw_text(gold_text, buf, Vector(col2_x, row3_y), ALIGN_LEFT, LAYER_GUI);
+  if (best_stats && (best_stats->time < time)) {
+    int csecs = (int)(best_stats->time * 100);
+    int mins = (int)(csecs / 6000);
+    int secs = (csecs % 6000) / 100;
+    snprintf(buf, sizeof(buf), "%02d:%02d", mins,secs);
+  }
+  context.draw_text(gold_text, buf, Vector(col3_x, row3_y), ALIGN_LEFT, LAYER_GUI);
+}
+
+void
+Statistics::zero()
+{
+  reset();
+  total_coins = 0;
+  total_badguys = 0;
+  total_secrets = 0;
+}
+
+void
+Statistics::reset()
+{
+  coins = 0;
+  badguys = 0;
+  time = 0;
+  secrets = 0;
+}
+
+void
+Statistics::merge(Statistics& s2)
+{
+  if (!s2.valid) return;
+  coins = std::max(coins, s2.coins);
+  total_coins = s2.total_coins;
+  badguys = std::max(badguys, s2.badguys);
+  total_badguys = s2.total_badguys;
+  time = std::min(time, s2.time);
+  secrets = std::max(secrets, s2.secrets);
+  total_secrets = s2.total_secrets;
+}
+
+void
+Statistics::operator+=(const Statistics& s2)
+{
+  if (!s2.valid) return;
+  if (s2.coins != nv_coins) coins += s2.coins;
+  if (s2.total_coins != nv_coins) total_coins += s2.total_coins;
+  if (s2.badguys != nv_badguys) badguys += s2.badguys;
+  if (s2.total_badguys != nv_badguys) total_badguys += s2.total_badguys;
+  if (s2.time != nv_time) time += s2.time;
+  if (s2.secrets != nv_secrets) secrets += s2.secrets;
+  if (s2.total_secrets != nv_secrets) total_secrets += s2.total_secrets;
+}
+
+void
+Statistics::declare_invalid()
+{
+  valid = false;
+}
diff --git a/src/statistics.hpp b/src/statistics.hpp
new file mode 100644 (file)
index 0000000..4aca2ad
--- /dev/null
@@ -0,0 +1,83 @@
+//  $Id$
+//
+//  SuperTux (Statistics module)
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//  Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SUPERTUX_STATISTICS_H
+#define SUPERTUX_STATISTICS_H
+
+#include <squirrel.h>
+#include "timer.hpp"
+
+namespace lisp { class Writer; }
+namespace lisp { class Lisp; }
+class Surface;
+class DrawingContext;
+
+/** This class is a layer between level and worldmap to keep
+ *  track of stuff like scores, and minor, but funny things, like
+ *  number of jumps and stuff */
+class Statistics
+{
+public:
+  int coins; /**< coins collected */
+  int total_coins; /**< coins in level */
+  int badguys; /**< badguys actively killed */
+  int total_badguys; /**< (vincible) badguys in level */
+  float time; /**< seconds needed */
+  int secrets; /**< secret areas found */
+  int total_secrets; /**< secret areas in level */
+
+public:
+  Statistics(); /**< Creates new statistics, call reset() before counting */
+  ~Statistics();
+
+  /// read statistics from lisp file
+  //void parse(const lisp::Lisp& lisp);
+  /// write statistics to lisp file
+  //void write(lisp::Writer& writer);
+
+  /**
+   * serialize statistics object as squirrel table "statistics"
+   */
+  void serialize_to_squirrel(HSQUIRRELVM vm);
+
+  /**
+   * unserialize statistics object from squirrel table "statistics"
+   */
+  void unserialize_from_squirrel(HSQUIRRELVM vm);
+
+  void draw_worldmap_info(DrawingContext& context); /**< draw worldmap stat HUD */
+  void draw_message_info(DrawingContext& context, std::string title); /**< draw stats at level start */
+  void draw_endseq_panel(DrawingContext& context, Statistics* best_stats, Surface* backdrop); /**< draw panel shown during level's end sequence */
+
+  void zero(); /**< Set stats to zero */
+  void reset(); /**< Set stats (but not totals) to zero */
+  void merge(Statistics& stats); /**< Given another Statistics object finds the best of each one */
+  void operator+=(const Statistics& o); /**< Add two Statistics objects */
+
+  void declare_invalid(); /**< marks statistics as invalid for their entire lifetime (e.g. after cheating). Invalid statistics will not be merged or drawn. */
+
+private:
+  bool valid; /**< stores whether this statistics can be trusted */
+  Timer timer; /**< for draw_worldmap_info: time until switching to next stat */
+  int display_stat; /**< for draw_worldmap_info: which stat is currently displayed */
+};
+
+#endif /*SUPERTUX_STATISTICS_H*/
diff --git a/src/textscroller.cpp b/src/textscroller.cpp
new file mode 100644 (file)
index 0000000..74dc067
--- /dev/null
@@ -0,0 +1,374 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include "textscroller.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "resources.hpp"
+#include "video/font.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "gui/menu.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "audio/sound_manager.hpp"
+#include "main.hpp"
+#include "fadeout.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+
+static const float DEFAULT_SPEED = 20;
+static const float LEFT_BORDER = 50;
+static const float SCROLL = 60;
+static const float ITEMS_SPACE = 4;
+
+TextScroller::TextScroller(const std::string& filename)
+{
+  defaultspeed = DEFAULT_SPEED;
+  speed = defaultspeed;
+
+  std::string text;
+  std::string background_file;
+
+  lisp::Parser parser;
+  try {
+    const lisp::Lisp* root = parser.parse(filename);
+
+    const lisp::Lisp* text_lisp = root->get_lisp("supertux-text");
+    if(!text_lisp)
+      throw std::runtime_error("File isn't a supertux-text file");
+
+    if(!text_lisp->get("text", text))
+      throw std::runtime_error("file doesn't contain a text field");
+    if(!text_lisp->get("background", background_file))
+      throw std::runtime_error("file doesn't contain a background file");
+    text_lisp->get("speed", defaultspeed);
+    text_lisp->get("music", music);
+  } catch(std::exception& e) {
+    std::ostringstream msg;
+    msg << "Couldn't load file '" << filename << "': " << e.what() << std::endl;
+    throw std::runtime_error(msg.str());
+  }
+
+  // Split text string lines into a vector
+  lines = InfoBoxLine::split(text, SCREEN_WIDTH - 2*LEFT_BORDER);
+
+  // load background image
+  background.reset(new Surface("images/background/" + background_file));
+
+  scroll = 0;
+  fading = false;
+}
+
+TextScroller::~TextScroller()
+{
+  for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) delete *i;
+}
+
+void
+TextScroller::setup()
+{
+  sound_manager->play_music(music);
+  Menu::set_current(NULL);
+}
+
+void
+TextScroller::update(float elapsed_time)
+{
+  if(main_controller->hold(Controller::UP)) {
+    speed = -defaultspeed*5;
+  } else if(main_controller->hold(Controller::DOWN)) {
+    speed = defaultspeed*5;
+  } else {
+    speed = defaultspeed;
+  }
+  if(main_controller->pressed(Controller::JUMP)
+      || main_controller->pressed(Controller::ACTION)
+      || main_controller->pressed(Controller::MENU_SELECT))
+    scroll += SCROLL;
+  if(main_controller->pressed(Controller::PAUSE_MENU)) {
+    main_loop->exit_screen(new FadeOut(0.5));
+  }
+
+  scroll += speed * elapsed_time;
+
+  if(scroll < 0)
+    scroll = 0;
+}
+
+void
+TextScroller::draw(DrawingContext& context)
+{
+  context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+      Color(0.6f, 0.7f, 0.8f, 0.5f), 0);
+  context.draw_surface(background.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 , SCREEN_HEIGHT/2 - background->get_height()/2), 0);
+
+  float y = SCREEN_HEIGHT - scroll;
+  for(size_t i = 0; i < lines.size(); i++) {
+    lines[i]->draw(context, Rect(LEFT_BORDER, y, SCREEN_WIDTH - 2*LEFT_BORDER, y), LAYER_GUI);
+    y += lines[i]->get_height();
+  }
+
+  if(y < 0 && !fading ) {
+    fading = true;
+    main_loop->exit_screen(new FadeOut(0.5));
+  }
+}
+
+InfoBox::InfoBox(const std::string& text)
+  : firstline(0)
+{
+  // Split text string lines into a vector
+  lines = InfoBoxLine::split(text, 400);
+
+  try
+  {
+    // get the arrow sprites
+    arrow_scrollup   = new Surface("images/engine/menu/scroll-up.png");
+    arrow_scrolldown = new Surface("images/engine/menu/scroll-down.png");
+  }
+  catch (std::exception& e)
+  {
+    log_warning << "Could not load scrolling images: " << e.what() << std::endl;
+    arrow_scrollup = 0;
+    arrow_scrolldown = 0;
+  }
+}
+
+InfoBox::~InfoBox()
+{
+  for(std::vector<InfoBoxLine*>::iterator i = lines.begin();
+      i != lines.end(); i++)
+    delete *i;
+  delete arrow_scrollup;
+  delete arrow_scrolldown;
+}
+
+void
+InfoBox::draw(DrawingContext& context)
+{
+  float x1 = SCREEN_WIDTH/2-200;
+  float y1 = SCREEN_HEIGHT/2-200;
+  float width = 400;
+  float height = 200;
+
+  context.draw_filled_rect(Vector(x1, y1), Vector(width, height),
+      Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-1);
+
+  float y = y1;
+  bool linesLeft = false;
+  for(size_t i = firstline; i < lines.size(); ++i) {
+    if(y >= y1 + height) {
+      linesLeft = true;
+      break;
+    }
+
+    lines[i]->draw(context, Rect(x1, y, x1+width, y), LAYER_GUI);
+    y += lines[i]->get_height();
+  }
+
+  {
+    // draw the scrolling arrows
+    if (arrow_scrollup && firstline > 0)
+      context.draw_surface(arrow_scrollup,
+      Vector( x1 + width  - arrow_scrollup->get_width(),  // top-right corner of box
+              y1), LAYER_GUI);
+
+    if (arrow_scrolldown && linesLeft && firstline < lines.size()-1)
+      context.draw_surface(arrow_scrolldown,
+      Vector( x1 + width  - arrow_scrolldown->get_width(),  // bottom-light corner of box
+              y1 + height - arrow_scrolldown->get_height()),
+              LAYER_GUI);
+  }
+}
+
+void
+InfoBox::scrollup()
+{
+  if(firstline > 0)
+    firstline--;
+}
+
+void
+InfoBox::scrolldown()
+{
+  if(firstline < lines.size()-1)
+    firstline++;
+}
+
+void
+InfoBox::pageup()
+{
+}
+
+void
+InfoBox::pagedown()
+{
+}
+
+namespace {
+Font* get_font_by_format_char(char format_char) {
+  switch(format_char)
+  {
+    case ' ':
+      return white_small_text;
+      break;
+    case '\t':
+      return white_text;
+      break;
+    case '-':
+      return white_big_text;
+      break;
+    case '*':
+      return blue_text;
+      break;
+    case '#':
+      return white_text;
+      break;
+    case '!':
+      return 0;
+      break;
+    default:
+      return 0;
+      log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+      break;
+  }
+}
+
+InfoBoxLine::LineType get_linetype_by_format_char(char format_char) {
+  switch(format_char)
+  {
+    case ' ':
+      return InfoBoxLine::SMALL;
+      break;
+    case '\t':
+      return InfoBoxLine::NORMAL;
+      break;
+    case '-':
+      return InfoBoxLine::HEADING;
+      break;
+    case '*':
+      return InfoBoxLine::REFERENCE;
+      break;
+    case '#':
+      return InfoBoxLine::NORMAL_LEFT;
+      break;
+    case '!':
+      return InfoBoxLine::IMAGE;
+      break;
+    default:
+      return InfoBoxLine::SMALL;
+      log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+      break;
+  }
+}
+}
+
+InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(white_text), text(text), image(0)
+{
+  font = get_font_by_format_char(format_char);
+  lineType = get_linetype_by_format_char(format_char);
+  if (lineType == IMAGE) image = new Surface(text);
+}
+
+InfoBoxLine::~InfoBoxLine()
+{
+  delete image;
+}
+
+const std::vector<InfoBoxLine*>
+InfoBoxLine::split(const std::string& text, float width)
+{
+  std::vector<InfoBoxLine*> lines;
+
+  std::string::size_type i = 0;
+  std::string::size_type l;
+  char format_char = '#';
+  while(i < text.size()) {
+    // take care of empty lines - represent them as blank lines of normal text
+    if (text[i] == '\n') {
+      lines.push_back(new InfoBoxLine('\t', ""));
+      i++;
+      continue;
+    }
+
+    // extract the format_char
+    format_char = text[i];
+    i++;
+    if (i >= text.size()) break;
+
+    // extract one line
+    l = text.find("\n", i);
+    if (l == std::string::npos) l=text.size();
+    std::string s = text.substr(i, l-i);
+    i = l+1;
+
+    // if we are dealing with an image, just store the line
+    if (format_char == '!') {
+      lines.push_back(new InfoBoxLine(format_char, s));
+      continue;
+    }
+
+    // append wrapped parts of line into list
+    std::string overflow;
+    do {
+      Font* font = get_font_by_format_char(format_char);
+      std::string s2 = s;
+      if (font) s2 = font->wrap_to_width(s2, width, &overflow);
+      lines.push_back(new InfoBoxLine(format_char, s2));
+      s = overflow;
+    } while (s.length() > 0);
+
+  }
+
+  return lines;
+}
+
+void
+InfoBoxLine::draw(DrawingContext& context, const Rect& bbox, int layer)
+{
+  Vector position = bbox.p1;
+  switch (lineType) {
+    case IMAGE:
+      context.draw_surface(image, Vector( (bbox.p1.x + bbox.p2.x - image->get_width()) / 2, position.y), layer);
+      break;
+    case NORMAL_LEFT:
+      context.draw_text(font, text, Vector(position.x, position.y), ALIGN_LEFT, layer);
+      break;
+    default:
+      context.draw_text(font, text, Vector((bbox.p1.x + bbox.p2.x) / 2, position.y), ALIGN_CENTER, layer);
+      break;
+  }
+}
+
+float
+InfoBoxLine::get_height()
+{
+  switch (lineType) {
+    case IMAGE:
+      return image->get_height() + ITEMS_SPACE;
+    case NORMAL_LEFT:
+      return font->get_height() + ITEMS_SPACE;
+    default:
+      return font->get_height() + ITEMS_SPACE;
+  }
+}
diff --git a/src/textscroller.hpp b/src/textscroller.hpp
new file mode 100644 (file)
index 0000000..cc166dc
--- /dev/null
@@ -0,0 +1,101 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef __TEXTSCROLLER_H__
+#define __TEXTSCROLLER_H__
+
+#include <vector>
+#include <string>
+#include <map>
+
+#include "screen.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class DrawingContext;
+class Surface;
+class Font;
+
+/**
+ * Helper class for InfoBox: Represents a line of text
+ */
+class InfoBoxLine
+{
+public:
+  enum LineType { NORMAL, NORMAL_LEFT, SMALL, HEADING, REFERENCE, IMAGE};
+
+  InfoBoxLine(char format_char, const std::string& text);
+  ~InfoBoxLine();
+
+  void draw(DrawingContext& context, const Rect& bbox, int layer);
+  float get_height();
+
+  static const std::vector<InfoBoxLine*> split(const std::string& text, float width);
+
+private:
+  InfoBoxLine::LineType lineType;
+  Font* font;
+  std::string text;
+  Surface* image;
+};
+
+/** This class is displaying a box with information text inside the game
+ */
+class InfoBox
+{
+public:
+  InfoBox(const std::string& text);
+  ~InfoBox();
+
+  void draw(DrawingContext& context);
+  void scrolldown();
+  void scrollup();
+  void pagedown();
+  void pageup();
+
+private:
+  size_t firstline;
+  std::vector<InfoBoxLine*> lines;
+  std::map<std::string, Surface*> images;
+  Surface* arrow_scrollup;
+  Surface* arrow_scrolldown;
+};
+
+class TextScroller : public Screen
+{
+public:
+  TextScroller(const std::string& file);
+  virtual ~TextScroller();
+
+  void setup();
+  void draw(DrawingContext& context);
+  void update(float elapsed_time);
+
+private:
+  float defaultspeed;
+  float speed;
+  std::string music;
+  std::auto_ptr<Surface> background;
+  std::vector<InfoBoxLine*> lines;
+  float scroll;
+  bool fading;
+};
+
+#endif
diff --git a/src/tile.cpp b/src/tile.cpp
new file mode 100644 (file)
index 0000000..c45f79c
--- /dev/null
@@ -0,0 +1,171 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include <iostream>
+#include <stdexcept>
+
+#include "lisp/lisp.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "timer.hpp"
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+
+
+Tile::Tile()
+  : id(0), attributes(0), data(0), anim_fps(1)
+{
+}
+
+Tile::Tile(unsigned int id, Uint32 attributes, const ImageSpec& imagespec)
+  : id(id), attributes(attributes), data(0), anim_fps(1)
+{
+  imagespecs.push_back(imagespec);
+}
+
+Tile::~Tile()
+{
+  for(std::vector<Surface*>::iterator i = images.begin(); i != images.end();
+      ++i) {
+    delete *i;
+  }
+}
+
+void
+Tile::parse(const lisp::Lisp& reader)
+{
+  if(!reader.get("id", id)) {
+    throw std::runtime_error("Missing tile-id.");
+  }
+
+  bool value = false;
+  if(reader.get("solid", value) && value)
+    attributes |= SOLID;
+  if(reader.get("unisolid", value) && value)
+    attributes |= UNISOLID | SOLID;
+  if(reader.get("brick", value) && value)
+    attributes |= BRICK;
+  if(reader.get("ice", value) && value)
+    attributes |= ICE;
+  if(reader.get("water", value) && value)
+    attributes |= WATER;
+  if(reader.get("hurts", value) && value)
+    attributes |= HURTS;
+  if(reader.get("fire", value) && value)
+    attributes |= FIRE;
+  if(reader.get("fullbox", value) && value)
+    attributes |= FULLBOX;
+  if(reader.get("coin", value) && value)
+    attributes |= COIN;
+  if(reader.get("goal", value) && value)
+    attributes |= GOAL;
+
+  if(reader.get("north", value) && value)
+    data |= WORLDMAP_NORTH;
+  if(reader.get("south", value) && value)
+    data |= WORLDMAP_SOUTH;
+  if(reader.get("west", value) && value)
+    data |= WORLDMAP_WEST;
+  if(reader.get("east", value) && value)
+    data |= WORLDMAP_EAST;
+  if(reader.get("stop", value) && value)
+    data |= WORLDMAP_STOP;
+
+  reader.get("data", data);
+  reader.get("anim-fps", anim_fps);
+
+  if(reader.get("slope-type", data)) {
+    attributes |= SOLID | SLOPE;
+  }
+
+  const lisp::Lisp* images = reader.get_lisp("images");
+  if(images)
+    parse_images(*images);
+}
+
+void
+Tile::parse_images(const lisp::Lisp& images_lisp)
+{
+  const lisp::Lisp* list = &images_lisp;
+  while(list) {
+    const lisp::Lisp* cur = list->get_car();
+    if(cur->get_type() == lisp::Lisp::TYPE_STRING) {
+      std::string file;
+      cur->get(file);
+      imagespecs.push_back(ImageSpec(file, Rect(0, 0, 0, 0)));
+    } else if(cur->get_type() == lisp::Lisp::TYPE_CONS &&
+                                 cur->get_car()->get_type() == lisp::Lisp::TYPE_SYMBOL &&
+              cur->get_car()->get_symbol() == "region") {
+      const lisp::Lisp* ptr = cur->get_cdr();
+
+      std::string file;
+      float x = 0, y = 0, w = 0, h = 0;
+      ptr->get_car()->get(file); ptr = ptr->get_cdr();
+      ptr->get_car()->get(x); ptr = ptr->get_cdr();
+      ptr->get_car()->get(y); ptr = ptr->get_cdr();
+      ptr->get_car()->get(w); ptr = ptr->get_cdr();
+      ptr->get_car()->get(h);
+      imagespecs.push_back(ImageSpec(file, Rect(x, y, x+w, y+h)));
+    } else {
+      log_warning << "Expected string or list in images tag" << std::endl;
+      continue;
+    }
+
+    list = list->get_cdr();
+  }
+}
+
+void
+Tile::load_images(const std::string& tilesetpath)
+{
+  assert(images.size() == 0);
+  for(std::vector<ImageSpec>::iterator i = imagespecs.begin(); i !=
+      imagespecs.end(); ++i) {
+    const ImageSpec& spec = *i;
+    Surface* surface;
+    std::string file = tilesetpath + spec.file;
+    if(spec.rect.get_width() <= 0) {
+      surface = new Surface(file);
+    } else {
+      surface = new Surface(file,
+          (int) spec.rect.p1.x,
+          (int) spec.rect.p1.y,
+          (int) spec.rect.get_width(),
+          (int) spec.rect.get_height());
+    }
+    images.push_back(surface);
+  }
+}
+
+void
+Tile::draw(DrawingContext& context, const Vector& pos, int z_pos) const
+{
+  if(images.size() > 1) {
+    size_t frame = size_t(game_time * anim_fps) % images.size();
+    context.draw_surface(images[frame], pos, z_pos);
+  } else if (images.size() == 1) {
+    context.draw_surface(images[0], pos, z_pos);
+  }
+}
diff --git a/src/tile.hpp b/src/tile.hpp
new file mode 100644 (file)
index 0000000..f0668d5
--- /dev/null
@@ -0,0 +1,157 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef TILE_H
+#define TILE_H
+
+#include <vector>
+#include <SDL.h>
+#include <stdint.h>
+#include "video/surface.hpp"
+#include "math/rect.hpp"
+
+namespace lisp { class Lisp; }
+
+class DrawingContext;
+
+/**
+Tile Class
+*/
+class Tile
+{
+public:
+  /// bitset for tile attributes
+  enum {
+    /** solid tile that is indestructable by Tux */
+    SOLID     = 0x0001,
+    /** uni-directional solid tile */
+    UNISOLID  = 0x0002,
+    /** a brick that can be destroyed by jumping under it */
+    BRICK     = 0x0004,
+    /** the level should be finished when touching a goaltile.
+     * if data is 0 then the endsequence should be triggered, if data is 1
+     * then we can finish the level instantly.
+     */
+    GOAL      = 0x0008,
+    /** slope tile */
+    SLOPE     = 0x0010,
+    /** Bonusbox, content is stored in \a data */
+    FULLBOX   = 0x0020,
+    /** Tile is a coin */
+    COIN      = 0x0040,
+
+    /* interesting flags (the following are passed to gameobjects) */
+    FIRST_INTERESTING_FLAG = 0x0100,
+
+    /** an ice brick that makes tux sliding more than usual */
+    ICE       = 0x0100,
+    /** a water tile in which tux starts to swim */
+    WATER     = 0x0200,
+    /** a tile that hurts the player if he touches it */
+    HURTS     = 0x0400,
+    /** for lava: WATER, HURTS, FIRE */
+    FIRE      = 0x0800
+  };
+
+  /// worldmap flags
+  enum {
+    WORLDMAP_NORTH = 0x0001,
+    WORLDMAP_SOUTH = 0x0002,
+    WORLDMAP_EAST  = 0x0004,
+    WORLDMAP_WEST  = 0x0008,
+       WORLDMAP_DIR_MASK = 0x000f,
+
+    WORLDMAP_STOP  = 0x0010,
+
+    // convenience values ("C" stands for crossroads)
+    WORLDMAP_CNSE  = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_EAST,
+    WORLDMAP_CNSW  = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_WEST,
+    WORLDMAP_CNEW  = WORLDMAP_NORTH | WORLDMAP_EAST  | WORLDMAP_WEST,
+    WORLDMAP_CSEW  = WORLDMAP_SOUTH | WORLDMAP_EAST  | WORLDMAP_WEST,
+    WORLDMAP_CNSEW = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_EAST | WORLDMAP_WEST
+  };
+
+  struct ImageSpec {
+    ImageSpec(const std::string& newfile, const Rect& newrect)
+      : file(newfile), rect(newrect)
+    { }
+
+    std::string file;
+    Rect rect;
+  };
+
+private:
+  unsigned int id;
+
+  std::vector<ImageSpec> imagespecs;
+  std::vector<Surface*> images;
+
+  /// tile attributes
+  uint32_t attributes;
+
+  /** General purpose data attached to a tile (content of a box, type of coin)*/
+  int data;
+
+  float anim_fps;
+
+public:
+  ~Tile();
+
+  /** Draw a tile on the screen */
+  void draw(DrawingContext& context, const Vector& pos, int z_pos) const;
+
+  unsigned int getID() const
+  { return id; }
+
+  uint32_t getAttributes() const
+  { return attributes; }
+
+  int getData() const
+  { return data; }
+
+  /// returns the width of the tile in pixels
+  int getWidth() const
+  {
+    if(!images.size())
+      return 0;
+    return (int) images[0]->get_width();
+  }
+
+  /// returns the height of the tiles in pixels
+  int getHeight() const
+  {
+    if(!images.size())
+      return 0;
+    return (int) images[0]->get_height();
+  }
+
+protected:
+  friend class TileManager;
+  Tile();
+  Tile(unsigned int id, Uint32 attributes, const ImageSpec& imagespec);
+
+  void load_images(const std::string& tilesetpath);
+
+  /// parses the tile and returns it's id number
+  void parse(const lisp::Lisp& reader);
+  void parse_images(const lisp::Lisp& cur);
+};
+
+#endif
diff --git a/src/tile_manager.cpp b/src/tile_manager.cpp
new file mode 100644 (file)
index 0000000..df1f90d
--- /dev/null
@@ -0,0 +1,173 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <memory>
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+#include <SDL.h>
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/list_iterator.hpp"
+#include "tile.hpp"
+#include "tile_manager.hpp"
+#include "resources.hpp"
+
+TileManager* tile_manager = NULL;
+
+TileManager::TileManager(const std::string& filename)
+{
+#ifdef DEBUG
+  Uint32 ticks = SDL_GetTicks();
+#endif
+  load_tileset(filename);
+#ifdef DEBUG
+  log_debug << "Tiles loaded in " << (SDL_GetTicks() - ticks) / 1000.0 << " seconds" << std::endl;
+#endif
+}
+
+TileManager::~TileManager()
+{
+  for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+    delete *i;
+}
+
+void TileManager::load_tileset(std::string filename)
+{
+  // free old tiles
+  for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+    delete *i;
+  tiles.clear();
+
+  std::string::size_type t = filename.rfind('/');
+  if(t == std::string::npos) {
+    tiles_path = "";
+  } else {
+    tiles_path = filename.substr(0, t+1);
+  }
+
+  lisp::Parser parser;
+  const lisp::Lisp* root = parser.parse(filename);
+
+  const lisp::Lisp* tiles_lisp = root->get_lisp("supertux-tiles");
+  if(!tiles_lisp)
+    throw std::runtime_error("file is not a supertux tiles file.");
+
+  lisp::ListIterator iter(tiles_lisp);
+  while(iter.next()) {
+    if(iter.item() == "tile") {
+      Tile* tile = new Tile();
+      tile->parse(*(iter.lisp()));
+
+      if(tile->id >= tiles.size())
+        tiles.resize(tile->id+1, 0);
+
+      if(tiles[tile->id] != 0) {
+        log_warning << "Tile with ID " << tile->id << " redefined" << std::endl;
+        delete tile;
+      } else {
+        tiles[tile->id] = tile;
+      }
+    } else if(iter.item() == "tilegroup") {
+      TileGroup tilegroup;
+      const lisp::Lisp* tilegroup_lisp = iter.lisp();
+      tilegroup_lisp->get("name", tilegroup.name);
+      tilegroup_lisp->get_vector("tiles", tilegroup.tiles);
+      tilegroups.insert(tilegroup);
+    } else if (iter.item() == "tiles") {
+      // List of ids (use 0 if the tile should be ignored)
+      std::vector<unsigned int> ids;
+      // List of attributes of the tile
+      std::vector<unsigned int> attributes;
+      std::string image;
+
+      // width and height of the image in tile units, this is used for two
+      // purposes:
+      //  a) so we don't have to load the image here to know its dimensions
+      //  b) so that the resulting 'tiles' entry is more robust,
+      //  ie. enlarging the image won't break the tile id mapping
+      // FIXME: height is actually not used, since width might be enough for
+      // all purposes, still feels somewhat more natural this way
+      unsigned int width  = 0;
+      unsigned int height = 0;
+
+      iter.lisp()->get_vector("ids",        ids);
+      iter.lisp()->get_vector("attributes", attributes);
+      iter.lisp()->get("image",      image);
+      iter.lisp()->get("width",      width);
+      iter.lisp()->get("height",     height);
+
+      if (ids.size() != attributes.size())
+        {
+          std::ostringstream err;
+          err << "Number of ids (" << ids.size() <<  ") and attributes (" << attributes.size()
+              << ") missmatch for image '" << image << "', but must be equal";
+          throw std::runtime_error(err.str());
+        }
+
+      for(std::vector<unsigned int>::size_type i = 0; i < ids.size() && i < width*height; ++i)
+        {
+          if (ids[i])
+            {
+              if(ids[i] >= tiles.size())
+                tiles.resize(ids[i]+1, 0);
+
+              int x = 32*(i % width);
+              int y = 32*(i / width);
+              Tile* tile = new Tile(ids[i], attributes[i], Tile::ImageSpec(image, Rect(x, y, x + 32, y + 32)));
+              if (tiles[ids[i]] == 0) {
+                tiles[ids[i]] = tile;
+              } else {
+                log_warning << "Tile with ID " << ids[i] << " redefined" << std::endl;
+                delete tile;
+              }
+            }
+        }
+
+    } else if(iter.item() == "properties") {
+      // deprecated
+    } else {
+      log_warning << "Unknown symbol '" << iter.item() << "' tile defintion file" << std::endl;
+    }
+  }
+
+  if (0)
+    { // enable this if you want to see a list of free tiles
+      log_info << "Last Tile ID is " << tiles.size()-1 << std::endl;
+      int last = -1;
+      for(int i = 0; i < int(tiles.size()); ++i)
+        {
+          if (tiles[i] == 0 && last == -1)
+            {
+              last = i;
+            }
+          else if (tiles[i] && last != -1)
+            {
+              log_info << "Free Tile IDs (" << i - last << "): " << last << " - " << i-1 << std::endl;
+              last = -1;
+            }
+        }
+    }
+}
diff --git a/src/tile_manager.hpp b/src/tile_manager.hpp
new file mode 100644 (file)
index 0000000..64906cf
--- /dev/null
@@ -0,0 +1,102 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef HEADER_TILE_MANAGER_HXX
+#define HEADER_TILE_MANAGER_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+#include <map>
+#include <iostream>
+#include <stdint.h>
+#include <assert.h>
+#include "log.hpp"
+#include "tile.hpp"
+
+struct TileGroup
+{
+  friend bool operator<(const TileGroup& lhs, const TileGroup& rhs)
+  { return lhs.name < rhs.name; };
+  friend bool operator>(const TileGroup& lhs, const TileGroup& rhs)
+  { return lhs.name > rhs.name; };
+
+  std::string name;
+  std::vector<int> tiles;
+};
+
+class TileManager
+{
+private:
+  typedef std::vector<Tile*> Tiles;
+  Tiles tiles;
+
+  static TileManager* instance_ ;
+  std::set<TileGroup> tilegroups;
+
+  std::string tiles_path;
+
+  void load_tileset(std::string filename);
+
+public:
+  TileManager(const std::string& filename);
+  ~TileManager();
+
+  const std::set<TileGroup>& get_tilegroups() const
+  {
+    return tilegroups;
+  }
+
+  const Tile* get(uint32_t id) const
+  {
+    //FIXME: Commenting out tiles in sprites.strf makes tiles.size() fail - it's being set to the first tile commented out.
+    assert(id < tiles.size());
+    Tile* tile = tiles[id];
+    if(!tile) {
+      log_warning << "Invalid tile: " << id << std::endl;
+      return tiles[0];
+    }
+
+    if(tile->images.size() == 0 && tile->imagespecs.size() != 0)
+      tile->load_images(tiles_path);
+
+    return tile;
+  }
+
+  uint32_t get_max_tileid() const
+  {
+    return tiles.size();
+  }
+
+  int get_default_width() const
+  {
+    return 32;
+  }
+
+  int get_default_height() const
+  {
+    return 32;
+  }
+};
+
+extern TileManager* tile_manager;
+
+#endif
diff --git a/src/timer.cpp b/src/timer.cpp
new file mode 100644 (file)
index 0000000..9c94f58
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <math.h>
+#include "timer.hpp"
+
+float game_time = 0;
+float real_time = 0;
+
+Timer::Timer()
+  : period(0), cycle_start(0), cyclic(false)
+{
+}
+
+Timer::~Timer()
+{
+}
+
+void
+Timer::start(float period, bool cyclic)
+{
+  this->period = period;
+  this->cyclic = cyclic;
+  cycle_start = game_time;
+}
+
+bool
+Timer::check()
+{
+  if(period == 0)
+    return false;
+
+  if(game_time - cycle_start >= period) {
+    if(cyclic) {
+      cycle_start = game_time - fmodf(game_time - cycle_start, period);
+    } else {
+      period = 0;
+    }
+    return true;
+  }
+
+  return false;
+}
diff --git a/src/timer.hpp b/src/timer.hpp
new file mode 100644 (file)
index 0000000..8dd9400
--- /dev/null
@@ -0,0 +1,64 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __SUPERTUX_TIMER_H__
+#define __SUPERTUX_TIMER_H__
+
+extern float game_time;
+extern float real_time;
+
+/**
+ * Simple timer designed to be used in the update functions of objects
+ */
+class Timer
+{
+public:
+  Timer();
+  ~Timer();
+
+  /** start the timer with the given period (in seconds).
+   * If cyclic=true then the timer willl be reset after each period.
+   * Set period to zero if you want to disable the timer.
+   */
+  void start(float period, bool cyclic = false);
+  /** returns true if a period (or more) passed since start call or last
+   * successfull check
+   */
+  bool check();
+  /** stop the timer */
+  void stop()
+  { start(0); }
+
+  /** returns the period of the timer or 0 if it isn't started */
+  float get_period() const
+  { return period; }
+  float get_timeleft() const
+  { return period - (game_time - cycle_start); }
+  float get_timegone() const
+  { return game_time - cycle_start; }
+  bool started() const
+  { return period != 0 && get_timeleft() > 0; }
+
+private:
+  float period;
+  float cycle_start;
+  bool cyclic;
+};
+
+#endif
diff --git a/src/tinygettext/findlocale.cpp b/src/tinygettext/findlocale.cpp
new file mode 100644 (file)
index 0000000..f559011
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ findlocale-0.46.tar.gz from http://icculus.org/~aspirin/findlocale/
+
+Copyright (C) 2004 Adam D. Moss (the "Author").  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 fur-
+nished 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, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+
+*/
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <winnt.h>
+#endif
+
+#ifdef MACOSX
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "findlocale.hpp"
+
+static int
+is_lcchar(const int c) {
+  return isalnum(c);
+}
+
+static void
+lang_country_variant_from_envstring(const char *str,
+                                    char **lang,
+                                    char **country,
+                                    char **variant) {
+  int end = 0;
+  int start;
+
+  /* get lang, if any */
+  start = end;
+  while (is_lcchar(str[end])) {
+    ++end;
+  }
+  if (start != end) {
+    int i;
+    int len = end - start;
+    char *s = (char*) malloc(len + 1);
+    for (i=0; i<len; ++i) {
+      s[i] = tolower(str[start + i]);
+    }
+    s[i] = '\0';
+    *lang = s;
+  } else {
+    *lang = NULL;
+  }
+
+  if (str[end] && str[end]!=':') { /* not at end of str */
+    ++end;
+  }
+
+  /* get country, if any */
+  start = end;
+  while (is_lcchar(str[end])) {
+    ++end;
+  }
+  if (start != end) {
+    int i;
+    int len = end - start;
+    char *s = (char*) malloc(len + 1);
+    for (i=0; i<len; ++i) {
+      s[i] = toupper(str[start + i]);
+    }
+    s[i] = '\0';
+    *country = s;
+  } else {
+    *country = NULL;
+  }
+
+  if (str[end] && str[end]!=':') { /* not at end of str */
+    ++end;
+  }
+
+  /* get variant, if any */
+  start = end;
+  while (str[end] && str[end]!=':') {
+    ++end;
+  }
+  if (start != end) {
+    int i;
+    int len = end - start;
+    char *s = (char*) malloc(len + 1);
+    for (i=0; i<len; ++i) {
+      s[i] = str[start + i];
+    }
+    s[i] = '\0';
+    *variant = s;
+  } else {
+    *variant = NULL;
+  }
+}
+
+
+static int
+accumulate_locstring(const char *str, FL_Locale *l) {
+  char *lang = NULL;
+  char *country = NULL;
+  char *variant = NULL;
+  if (str) {
+    lang_country_variant_from_envstring(str, &lang, &country, &variant);
+    if (lang) {
+      l->lang = lang;
+      l->country = country;
+      l->variant = variant;
+      return 1;
+    }
+  }
+  free(lang); free(country); free(variant);
+  return 0;
+}
+
+
+#ifndef WIN32
+static int
+accumulate_env(const char *name, FL_Locale *l) {
+  char *env;
+  char *lang = NULL;
+  char *country = NULL;
+  char *variant = NULL;
+  env = getenv(name);
+  if (env) {
+    return accumulate_locstring(env, l);
+  }
+  free(lang); free(country); free(variant);
+  return 0;
+}
+#endif
+
+static void
+canonise_fl(FL_Locale *l) {
+  /* this function fixes some common locale-specifying mistakes */
+  /* en_UK -> en_GB */
+  if (l->lang && 0 == strcmp(l->lang, "en")) {
+    if (l->country && 0 == strcmp(l->country, "UK")) {
+      free((void*)l->country);
+      l->country = strdup("GB");
+    }
+  }
+  /* ja_JA -> ja_JP */
+  if (l->lang && 0 == strcmp(l->lang, "ja")) {
+    if (l->country && 0 == strcmp(l->country, "JA")) {
+      free((void*)l->country);
+      l->country = strdup("JP");
+    }
+  }
+}
+
+
+#ifdef WIN32
+#include <stdio.h>
+#define ML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##pn##_##sn)
+#define MLN(pn) MAKELANGID(LANG_##pn, SUBLANG_DEFAULT)
+#define RML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##sn)
+typedef struct {
+  LANGID id;
+  char*  code;
+} IDToCode;
+static const IDToCode both_to_code[] = {
+  {ML(ENGLISH,US),           "en_US.ISO_8859-1"},
+  {ML(ENGLISH,CAN),          "en_CA"}, /* english / canadian */
+  {ML(ENGLISH,UK),           "en_GB"},
+  {ML(ENGLISH,EIRE),         "en_IE"},
+  {ML(ENGLISH,AUS),          "en_AU"},
+  {MLN(GERMAN),              "de_DE"},
+  {MLN(SPANISH),             "es_ES"},
+  {ML(SPANISH,MEXICAN),      "es_MX"},
+  {MLN(FRENCH),              "fr_FR"},
+  {ML(FRENCH,CANADIAN),      "fr_CA"},
+  {ML(FRENCH,BELGIAN),       "fr_BE"}, /* ? */
+  {ML(DUTCH,BELGIAN),        "nl_BE"}, /* ? */
+  {ML(PORTUGUESE,BRAZILIAN), "pt_BR"},
+  {MLN(PORTUGUESE),          "pt_PT"},
+  {MLN(SWEDISH),             "sv_SE"},
+  {ML(CHINESE,HONGKONG),     "zh_HK"},
+  /* these are machine-generated and not yet verified */
+  {RML(AFRIKAANS,DEFAULT), "af_ZA"},
+  {RML(ALBANIAN,DEFAULT), "sq_AL"},
+  {RML(ARABIC,ARABIC_ALGERIA), "ar_DZ"},
+  {RML(ARABIC,ARABIC_BAHRAIN), "ar_BH"},
+  {RML(ARABIC,ARABIC_EGYPT), "ar_EG"},
+  {RML(ARABIC,ARABIC_IRAQ), "ar_IQ"},
+  {RML(ARABIC,ARABIC_JORDAN), "ar_JO"},
+  {RML(ARABIC,ARABIC_KUWAIT), "ar_KW"},
+  {RML(ARABIC,ARABIC_LEBANON), "ar_LB"},
+  {RML(ARABIC,ARABIC_LIBYA), "ar_LY"},
+  {RML(ARABIC,ARABIC_MOROCCO), "ar_MA"},
+  {RML(ARABIC,ARABIC_OMAN), "ar_OM"},
+  {RML(ARABIC,ARABIC_QATAR), "ar_QA"},
+  {RML(ARABIC,ARABIC_SAUDI_ARABIA), "ar_SA"},
+  {RML(ARABIC,ARABIC_SYRIA), "ar_SY"},
+  {RML(ARABIC,ARABIC_TUNISIA), "ar_TN"},
+  {RML(ARABIC,ARABIC_UAE), "ar_AE"},
+  {RML(ARABIC,ARABIC_YEMEN), "ar_YE"},
+  {RML(ARMENIAN,DEFAULT), "hy_AM"},
+  {RML(AZERI,AZERI_CYRILLIC), "az_AZ"},
+  {RML(AZERI,AZERI_LATIN), "az_AZ"},
+  {RML(BASQUE,DEFAULT), "eu_ES"},
+  {RML(BELARUSIAN,DEFAULT), "be_BY"},
+/*{RML(BRETON,DEFAULT), "br_FR"},*/
+  {RML(BULGARIAN,DEFAULT), "bg_BG"},
+  {RML(CATALAN,DEFAULT), "ca_ES"},
+  {RML(CHINESE,CHINESE_HONGKONG), "zh_HK"},
+  {RML(CHINESE,CHINESE_MACAU), "zh_MO"},
+  {RML(CHINESE,CHINESE_SIMPLIFIED), "zh_CN"},
+  {RML(CHINESE,CHINESE_SINGAPORE), "zh_SG"},
+  {RML(CHINESE,CHINESE_TRADITIONAL), "zh_TW"},
+/*{RML(CORNISH,DEFAULT), "kw_GB"},*/
+  {RML(CZECH,DEFAULT), "cs_CZ"},
+  {RML(DANISH,DEFAULT), "da_DK"},
+  {RML(DUTCH,DUTCH), "nl_NL"},
+  {RML(DUTCH,DUTCH_BELGIAN), "nl_BE"},
+/*{RML(DUTCH,DUTCH_SURINAM), "nl_SR"},*/
+  {RML(ENGLISH,ENGLISH_AUS), "en_AU"},
+  {RML(ENGLISH,ENGLISH_BELIZE), "en_BZ"},
+  {RML(ENGLISH,ENGLISH_CAN), "en_CA"},
+  {RML(ENGLISH,ENGLISH_CARIBBEAN), "en_CB"},
+  {RML(ENGLISH,ENGLISH_EIRE), "en_IE"},
+  {RML(ENGLISH,ENGLISH_JAMAICA), "en_JM"},
+  {RML(ENGLISH,ENGLISH_NZ), "en_NZ"},
+  {RML(ENGLISH,ENGLISH_PHILIPPINES), "en_PH"},
+  {RML(ENGLISH,ENGLISH_SOUTH_AFRICA), "en_ZA"},
+  {RML(ENGLISH,ENGLISH_TRINIDAD), "en_TT"},
+  {RML(ENGLISH,ENGLISH_UK), "en_GB"},
+  {RML(ENGLISH,ENGLISH_US), "en_US"},
+  {RML(ENGLISH,ENGLISH_ZIMBABWE), "en_ZW"},
+/*{RML(ESPERANTO,DEFAULT), "eo_"},*/
+  {RML(ESTONIAN,DEFAULT), "et_EE"},
+  {RML(FAEROESE,DEFAULT), "fo_FO"},
+  {RML(FARSI,DEFAULT), "fa_IR"},
+  {RML(FINNISH,DEFAULT), "fi_FI"},
+  {RML(FRENCH,FRENCH), "fr_FR"},
+  {RML(FRENCH,FRENCH_BELGIAN), "fr_BE"},
+  {RML(FRENCH,FRENCH_CANADIAN), "fr_CA"},
+  {RML(FRENCH,FRENCH_LUXEMBOURG), "fr_LU"},
+  {RML(FRENCH,FRENCH_MONACO), "fr_MC"},
+  {RML(FRENCH,FRENCH_SWISS), "fr_CH"},
+/*{RML(GAELIC,GAELIC), "ga_IE"},*/
+/*{RML(GAELIC,GAELIC_MANX), "gv_GB"},*/
+/*{RML(GAELIC,GAELIC_SCOTTISH), "gd_GB"},*/
+/*{RML(GALICIAN,DEFAULT), "gl_ES"},*/
+  {RML(GEORGIAN,DEFAULT), "ka_GE"},
+  {RML(GERMAN,GERMAN), "de_DE"},
+  {RML(GERMAN,GERMAN_AUSTRIAN), "de_AT"},
+  {RML(GERMAN,GERMAN_LIECHTENSTEIN), "de_LI"},
+  {RML(GERMAN,GERMAN_LUXEMBOURG), "de_LU"},
+  {RML(GERMAN,GERMAN_SWISS), "de_CH"},
+  {RML(GREEK,DEFAULT), "el_GR"},
+  {RML(GUJARATI,DEFAULT), "gu_IN"},
+  {RML(HEBREW,DEFAULT), "he_IL"},
+  {RML(HINDI,DEFAULT), "hi_IN"},
+  {RML(HUNGARIAN,DEFAULT), "hu_HU"},
+  {RML(ICELANDIC,DEFAULT), "is_IS"},
+  {RML(INDONESIAN,DEFAULT), "id_ID"},
+  {RML(ITALIAN,ITALIAN), "it_IT"},
+  {RML(ITALIAN,ITALIAN_SWISS), "it_CH"},
+  {RML(JAPANESE,DEFAULT), "ja_JP"},
+  {RML(KANNADA,DEFAULT), "kn_IN"},
+  {RML(KAZAK,DEFAULT), "kk_KZ"},
+  {RML(KONKANI,DEFAULT), "kok_IN"},
+  {RML(KOREAN,KOREAN), "ko_KR"},
+/*{RML(KYRGYZ,DEFAULT), "ky_KG"},*/
+  {RML(LATVIAN,DEFAULT), "lv_LV"},
+  {RML(LITHUANIAN,LITHUANIAN), "lt_LT"},
+  {RML(MACEDONIAN,DEFAULT), "mk_MK"},
+  {RML(MALAY,MALAY_BRUNEI_DARUSSALAM), "ms_BN"},
+  {RML(MALAY,MALAY_MALAYSIA), "ms_MY"},
+  {RML(MARATHI,DEFAULT), "mr_IN"},
+/*{RML(MONGOLIAN,DEFAULT), "mn_MN"},*/
+  {RML(NORWEGIAN,NORWEGIAN_BOKMAL), "nb_NO"},
+  {RML(NORWEGIAN,NORWEGIAN_NYNORSK), "nn_NO"},
+  {RML(POLISH,DEFAULT), "pl_PL"},
+  {RML(PORTUGUESE,PORTUGUESE), "pt_PT"},
+  {RML(PORTUGUESE,PORTUGUESE_BRAZILIAN), "pt_BR"},
+  {RML(PUNJABI,DEFAULT), "pa_IN"},
+  {RML(ROMANIAN,DEFAULT), "ro_RO"},
+  {RML(RUSSIAN,DEFAULT), "ru_RU"},
+  {RML(SANSKRIT,DEFAULT), "sa_IN"},
+  {RML(SERBIAN,DEFAULT), "hr_HR"},
+  {RML(SERBIAN,SERBIAN_CYRILLIC), "sr_SP"},
+  {RML(SERBIAN,SERBIAN_LATIN), "sr_SP"},
+  {RML(SLOVAK,DEFAULT), "sk_SK"},
+  {RML(SLOVENIAN,DEFAULT), "sl_SI"},
+  {RML(SPANISH,SPANISH), "es_ES"},
+  {RML(SPANISH,SPANISH_ARGENTINA), "es_AR"},
+  {RML(SPANISH,SPANISH_BOLIVIA), "es_BO"},
+  {RML(SPANISH,SPANISH_CHILE), "es_CL"},
+  {RML(SPANISH,SPANISH_COLOMBIA), "es_CO"},
+  {RML(SPANISH,SPANISH_COSTA_RICA), "es_CR"},
+  {RML(SPANISH,SPANISH_DOMINICAN_REPUBLIC), "es_DO"},
+  {RML(SPANISH,SPANISH_ECUADOR), "es_EC"},
+  {RML(SPANISH,SPANISH_EL_SALVADOR), "es_SV"},
+  {RML(SPANISH,SPANISH_GUATEMALA), "es_GT"},
+  {RML(SPANISH,SPANISH_HONDURAS), "es_HN"},
+  {RML(SPANISH,SPANISH_MEXICAN), "es_MX"},
+  {RML(SPANISH,SPANISH_MODERN), "es_ES"},
+  {RML(SPANISH,SPANISH_NICARAGUA), "es_NI"},
+  {RML(SPANISH,SPANISH_PANAMA), "es_PA"},
+  {RML(SPANISH,SPANISH_PARAGUAY), "es_PY"},
+  {RML(SPANISH,SPANISH_PERU), "es_PE"},
+  {RML(SPANISH,SPANISH_PUERTO_RICO), "es_PR"},
+  {RML(SPANISH,SPANISH_URUGUAY), "es_UY"},
+  {RML(SPANISH,SPANISH_VENEZUELA), "es_VE"},
+  {RML(SWAHILI,DEFAULT), "sw_KE"},
+  {RML(SWEDISH,SWEDISH), "sv_SE"},
+  {RML(SWEDISH,SWEDISH_FINLAND), "sv_FI"},
+/*{RML(SYRIAC,DEFAULT), "syr_SY"},*/
+  {RML(TAMIL,DEFAULT), "ta_IN"},
+  {RML(TATAR,DEFAULT), "tt_TA"},
+  {RML(TELUGU,DEFAULT), "te_IN"},
+  {RML(THAI,DEFAULT), "th_TH"},
+  {RML(TURKISH,DEFAULT), "tr_TR"},
+  {RML(UKRAINIAN,DEFAULT), "uk_UA"},
+  {RML(URDU,URDU_PAKISTAN), "ur_PK"},
+  {RML(UZBEK,UZBEK_CYRILLIC), "uz_UZ"},
+  {RML(UZBEK,UZBEK_LATIN), "uz_UZ"},
+  {RML(VIETNAMESE,DEFAULT), "vi_VN"},
+/*{RML(WALON,DEFAULT), "wa_BE"},*/
+/*{RML(WELSH,DEFAULT), "cy_GB"},*/
+};
+static const IDToCode primary_to_code[] = {
+  {LANG_AFRIKAANS,  "af"},
+  {LANG_ARABIC,     "ar"},
+  {LANG_AZERI,      "az"},
+  {LANG_BULGARIAN,  "bg"},
+/*{LANG_BRETON,     "br"},*/
+  {LANG_BELARUSIAN, "by"},
+  {LANG_CATALAN,    "ca"},
+  {LANG_CZECH,      "cs"},
+/*{LANG_WELSH,      "cy"},*/
+  {LANG_DANISH,     "da"},
+  {LANG_GERMAN,     "de"},
+  {LANG_GREEK,      "el"},
+  {LANG_ENGLISH,    "en"},
+/*{LANG_ESPERANTO,  "eo"},*/
+  {LANG_SPANISH,    "es"},
+  {LANG_ESTONIAN,   "et"},
+  {LANG_BASQUE,     "eu"},
+  {LANG_FARSI,      "fa"},
+  {LANG_FINNISH,    "fi"},
+  {LANG_FAEROESE,   "fo"},
+  {LANG_FRENCH,     "fr"},
+/*{LANG_GAELIC,     "ga"},*/
+/*{LANG_GALICIAN,   "gl"},*/
+  {LANG_GUJARATI,   "gu"},
+  {LANG_HEBREW,     "he"},
+  {LANG_HINDI,      "hi"},
+  {LANG_SERBIAN,    "hr"},
+  {LANG_HUNGARIAN,  "hu"},
+  {LANG_ARMENIAN,   "hy"},
+  {LANG_INDONESIAN, "id"},
+  {LANG_ITALIAN,    "it"},
+  {LANG_JAPANESE,   "ja"},
+  {LANG_GEORGIAN,   "ka"},
+  {LANG_KAZAK,      "kk"},
+  {LANG_KANNADA,    "kn"},
+  {LANG_KOREAN,     "ko"},
+/*{LANG_KYRGYZ,     "ky"},*/
+  {LANG_LITHUANIAN, "lt"},
+  {LANG_LATVIAN,    "lv"},
+  {LANG_MACEDONIAN, "mk"},
+/*{LANG_MONGOLIAN,  "mn"},*/
+  {LANG_MARATHI,    "mr"},
+  {LANG_MALAY,      "ms"},
+  {LANG_NORWEGIAN,  "nb"},
+  {LANG_DUTCH,      "nl"},
+  {LANG_NORWEGIAN,  "nn"},
+  {LANG_NORWEGIAN,  "no"},/* unofficial? */
+  {LANG_PUNJABI,    "pa"},
+  {LANG_POLISH,     "pl"},
+  {LANG_PORTUGUESE, "pt"},
+  {LANG_ROMANIAN,   "ro"},
+  {LANG_RUSSIAN,    "ru"},
+  {LANG_SLOVAK,     "sk"},
+  {LANG_SLOVENIAN,  "sl"},
+  {LANG_ALBANIAN,   "sq"},
+  {LANG_SERBIAN,    "sr"},
+  {LANG_SWEDISH,    "sv"},
+  {LANG_SWAHILI,    "sw"},
+  {LANG_TAMIL,      "ta"},
+  {LANG_THAI,       "th"},
+  {LANG_TURKISH,    "tr"},
+  {LANG_TATAR,      "tt"},
+  {LANG_UKRAINIAN,  "uk"},
+  {LANG_URDU,       "ur"},
+  {LANG_UZBEK,      "uz"},
+  {LANG_VIETNAMESE, "vi"},
+/*{LANG_WALON,      "wa"},*/
+  {LANG_CHINESE,    "zh"},
+};
+static int num_primary_to_code =
+  sizeof(primary_to_code) / sizeof(*primary_to_code);
+static int num_both_to_code =
+  sizeof(both_to_code) / sizeof(*both_to_code);
+
+static const int
+lcid_to_fl(LCID lcid,
+           FL_Locale *rtn) {
+  LANGID langid       = LANGIDFROMLCID(lcid);
+  LANGID primary_lang = PRIMARYLANGID(langid);
+#if 0
+  LANGID sub_lang     = SUBLANGID(langid);
+#endif
+  int i;
+  /* try to find an exact primary/sublanguage combo that we know about */
+  for (i=0; i<num_both_to_code; ++i) {
+    if (both_to_code[i].id == langid) {
+      accumulate_locstring(both_to_code[i].code, rtn);
+      return 1;
+    }
+  }
+  /* fallback to just checking the primary language id */
+  for (i=0; i<num_primary_to_code; ++i) {
+    if (primary_to_code[i].id == primary_lang) {
+      accumulate_locstring(primary_to_code[i].code, rtn);
+      return 1;
+    }
+  }
+  return 0;
+}
+#endif
+
+
+FL_Success
+FL_FindLocale(FL_Locale **locale, FL_Domain /*domain*/) {
+  FL_Success success = FL_FAILED;
+  FL_Locale *rtn = (FL_Locale*) malloc(sizeof(FL_Locale));
+  rtn->lang = NULL;
+  rtn->country = NULL;
+  rtn->variant = NULL;
+
+#ifdef WIN32
+  /* win32 >= mswindows95 */
+  {
+    LCID lcid = GetThreadLocale();
+    if (lcid_to_fl(lcid, rtn)) {
+      success = FL_CONFIDENT;
+    }
+    if (success == FL_FAILED) {
+      /* assume US English on mswindows systems unless we know otherwise */
+      if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
+        success = FL_DEFAULT_GUESS;
+      }
+    }
+  }
+#else
+  /* assume unixoid */
+  {
+#ifdef MACOSX
+    CFIndex sz;
+    CFArrayRef languages;
+    CFStringRef uxstylelangs;
+    char *uxsl;
+
+    /* get the languages from the user's presets */
+    languages = (CFArrayRef)CFPreferencesCopyValue(CFSTR("AppleLanguages"),
+      kCFPreferencesAnyApplication, kCFPreferencesCurrentUser,
+      kCFPreferencesAnyHost);
+
+    /* join the returned string array into a string separated by colons */
+    uxstylelangs = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
+      languages, CFSTR(":"));
+
+    /* convert this string into a C string */
+    sz = CFStringGetLength(uxstylelangs) + 1;
+    uxsl = (char*)malloc(sz);
+    CFStringGetCString(uxstylelangs, uxsl, sz, kCFStringEncodingISOLatin1);
+
+    /* add it to the list */
+    if (accumulate_locstring(uxsl, rtn)) {
+      success = FL_CONFIDENT;
+    }
+    /* continue the UNIX method */
+#endif
+    /* examples: */
+    /* sv_SE.ISO_8859-1 */
+    /* fr_FR.ISO8859-1 */
+    /* no_NO_NB */
+    /* no_NO_NY */
+    /* no_NO */
+    /* de_DE */
+    /* try the various vars in decreasing order of authority */
+    if (accumulate_env("LC_ALL", rtn) ||
+        accumulate_env("LC_MESSAGES", rtn) ||
+        accumulate_env("LANG", rtn) ||
+        accumulate_env("LANGUAGE", rtn)) {
+      success = FL_CONFIDENT;
+    }
+    if (success == FL_FAILED) {
+      /* assume US English on unixoid systems unless we know otherwise */
+      if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
+        success = FL_DEFAULT_GUESS;
+      }
+    }
+  }
+#endif
+
+  if (success != FL_FAILED) {
+    canonise_fl(rtn);
+  }
+
+  *locale = rtn;
+  return success;
+}
+
+
+void
+FL_FreeLocale(FL_Locale **locale) {
+  if (locale) {
+    FL_Locale *l = *locale;
+    if (l) {
+      if (l->lang) {
+        free((void*)l->lang);
+      }
+      if (l->country) {
+        free((void*)l->country);
+      }
+      if (l->variant) {
+        free((void*)l->variant);
+      }
+      free(l);
+      *locale = NULL;
+    }
+  }
+}
diff --git a/src/tinygettext/findlocale.hpp b/src/tinygettext/findlocale.hpp
new file mode 100644 (file)
index 0000000..ab4d847
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ findlocale-0.46.tar.gz from http://icculus.org/~aspirin/findlocale/
+
+Copyright (C) 2004 Adam D. Moss (the "Author").  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 fur-
+nished 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, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+
+*/
+
+#ifndef __findlocale_h_
+#define __findlocale_h_
+
+typedef const char* FL_Lang;
+typedef const char* FL_Country;
+typedef const char* FL_Variant;
+
+typedef struct {
+  FL_Lang    lang;
+  FL_Country country;
+  FL_Variant variant;
+} FL_Locale;
+
+typedef enum {
+  /* for some reason we failed to even guess: this should never happen */
+  FL_FAILED        = 0,
+  /* couldn't query locale -- returning a guess (almost always English) */
+  FL_DEFAULT_GUESS = 1,
+  /* the returned locale type was found by successfully asking the system */
+  FL_CONFIDENT     = 2
+} FL_Success;
+
+typedef enum {
+  FL_MESSAGES = 0
+} FL_Domain;
+
+/* This allocates/fills in a FL_Locale structure with pointers to
+   strings (which should be treated as static), or NULL for inappropriate /
+   undetected fields. */
+FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain);
+/* This should be used to free the struct written by FL_FindLocale */
+void FL_FreeLocale(FL_Locale **locale);
+
+#endif /*__findlocale_h_*/
diff --git a/src/tinygettext/tinygettext.cpp b/src/tinygettext/tinygettext.cpp
new file mode 100644 (file)
index 0000000..d22ae2a
--- /dev/null
@@ -0,0 +1,787 @@
+//  $Id$
+//
+//  TinyGetText
+//  Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <fstream>
+#include <iostream>
+#include <algorithm>
+#include <ctype.h>
+#include <errno.h>
+
+#include "SDL.h"
+
+#include "tinygettext.hpp"
+#include "log.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "log.hpp"
+#include "findlocale.hpp"
+
+//#define TRANSLATION_DEBUG
+
+namespace TinyGetText {
+
+/** Convert \a which is in \a from_charset to \a to_charset and return it */
+std::string convert(const std::string& text,
+                    const std::string& from_charset,
+                    const std::string& to_charset)
+{
+  if (from_charset == to_charset)
+    return text;
+
+  char *in = new char[text.length() + 1];
+  strcpy(in, text.c_str());
+  char *out = SDL_iconv_string(to_charset.c_str(), from_charset.c_str(), in, text.length() + 1);
+  delete[] in; 
+  if(out == 0)
+  {
+    log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " failed" << std::endl;
+    return "";
+  }
+  std::string ret(out);
+  SDL_free(out);
+  return ret;
+#if 0
+  iconv_t cd = SDL_iconv_open(to_charset.c_str(), from_charset.c_str());
+
+  size_t in_len = text.length();
+  size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough
+
+  char*  out_orig = new char[out_len];
+  char*  in_orig  = new char[in_len+1];
+  strcpy(in_orig, text.c_str());
+
+  char* out = out_orig;
+  ICONV_CONST char* in  = in_orig;
+  size_t out_len_temp = out_len; // iconv is counting down the bytes it has
+                                 // written from this...
+
+  size_t retval = SDL_iconv(cd, &in, &in_len, &out, &out_len_temp);
+  out_len -= out_len_temp; // see above
+  if (retval == (size_t) -1)
+    {
+      log_warning << strerror(errno) << std::endl;
+      log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " went wrong: " << retval << std::endl;
+      return "";
+    }
+  SDL_iconv_close(cd);
+
+  std::string ret(out_orig, out_len);
+  delete[] out_orig;
+  delete[] in_orig;
+  return ret;
+#endif
+}
+
+bool has_suffix(const std::string& lhs, const std::string rhs)
+{
+  if (lhs.length() < rhs.length())
+    return false;
+  else
+    return lhs.compare(lhs.length() - rhs.length(), rhs.length(), rhs) == 0;
+}
+
+bool has_prefix(const std::string& lhs, const std::string rhs)
+{
+  if (lhs.length() < rhs.length())
+    return false;
+  else
+    return lhs.compare(0, rhs.length(), rhs) == 0;
+}
+
+int plural1(int )     { return 0; }
+int plural2_1(int n)  { return (n != 1); }
+int plural2_2(int n)  { return (n > 1); }
+int plural3_lv(int n) { return (n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2); }
+int plural3_ga(int n) { return n==1 ? 0 : n==2 ? 1 : 2; }
+int plural3_lt(int n) { return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_1(int n)  { return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_sk(int n) { return (n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; }
+int plural3_pl(int n) { return (n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_sl(int n) { return (n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3); }
+
+/** Language Definitions */
+//*{
+LanguageDef lang_hu("hu", "Hungarian",         1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_ja("ja", "Japanese",          1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_ko("ko", "Korean",            1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_tr("tr", "Turkish",           1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_da("da", "Danish",            2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nl("nl", "Dutch",             2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_en("en", "English",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fo("fo", "Faroese",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_de("de", "German",            2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nb("nb", "Norwegian Bokmal",  2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_no("no", "Norwegian",         2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nn("nn", "Norwegian Nynorsk", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_sv("sv", "Swedish",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_et("et", "Estonian",          2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fi("fi", "Finnish",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_el("el", "Greek",             2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_he("he", "Hebrew",            2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_it("it", "Italian",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_pt("pt", "Portuguese",        2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_es("es", "Spanish",           2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_eo("eo", "Esperanto",         2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fr("fr", "French",            2, plural2_2); // "nplurals=2; plural=(n > 1);"
+LanguageDef lang_pt_BR("pt_BR", "Brazilian",   2, plural2_2); // "nplurals=2; plural=(n > 1);"
+LanguageDef lang_lv("lv", "Latvian",           3, plural3_lv); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"
+LanguageDef lang_ga("ga", "Irish",             3, plural3_ga); // "nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;"
+LanguageDef lang_lt("lt", "Lithuanian",        3, plural3_lt); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_hr("hr", "Croatian",          3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_cs("cs", "Czech",             3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_ru("ru", "Russian",           3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_uk("uk", "Ukrainian",         3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_sk("sk", "Slovak",            3, plural3_sk); // "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"
+LanguageDef lang_pl("pl", "Polish",            3, plural3_pl); // "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
+LanguageDef lang_sl("sl", "Slovenian",         3, plural3_sl); // "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
+//*}
+
+LanguageDef&
+get_language_def(const std::string& name)
+{
+  if (name == "hu") return lang_hu;
+  else if (name == "ja") return lang_ja;
+  else if (name == "ko") return lang_ko;
+  else if (name == "tr") return lang_tr;
+  else if (name == "da") return lang_da;
+  else if (name == "nl") return lang_nl;
+  else if (name == "en") return lang_en;
+  else if (name == "fo") return lang_fo;
+  else if (name == "de") return lang_de;
+  else if (name == "nb") return lang_nb;
+  else if (name == "no") return lang_no;
+  else if (name == "nn") return lang_nn;
+  else if (name == "sv") return lang_sv;
+  else if (name == "et") return lang_et;
+  else if (name == "fi") return lang_fi;
+  else if (name == "el") return lang_el;
+  else if (name == "he") return lang_he;
+  else if (name == "it") return lang_it;
+  else if (name == "pt") return lang_pt;
+  else if (name == "es") return lang_es;
+  else if (name == "eo") return lang_eo;
+  else if (name == "fr") return lang_fr;
+  else if (name == "pt_BR") return lang_pt_BR;
+  else if (name == "lv") return lang_lv;
+  else if (name == "ga") return lang_ga;
+  else if (name == "lt") return lang_lt;
+  else if (name == "hr") return lang_hr;
+  else if (name == "cs") return lang_cs;
+  else if (name == "ru") return lang_ru;
+  else if (name == "uk") return lang_uk;
+  else if (name == "sk") return lang_sk;
+  else if (name == "pl") return lang_pl;
+  else if (name == "sl") return lang_sl;
+  else return lang_en;
+}
+
+DictionaryManager::DictionaryManager()
+  : current_dict(&empty_dict)
+{
+  parseLocaleAliases();
+  // Environment variable SUPERTUX_LANG overrides language settings.
+  const char* lang = getenv( "SUPERTUX_LANG" );
+  if( lang ){
+    set_language( lang );
+    return;
+  }
+  // use findlocale to setup language
+  FL_Locale *locale;
+  FL_FindLocale( &locale, FL_MESSAGES );
+  if(locale->lang) {
+    if (locale->country) {
+      set_language( std::string(locale->lang)+"_"+std::string(locale->country) );
+    } else {
+      set_language( std::string(locale->lang) );
+    }
+  }
+  FL_FreeLocale( &locale );
+}
+
+void
+DictionaryManager::parseLocaleAliases()
+{
+  // try to parse language alias list
+  std::ifstream in("/usr/share/locale/locale.alias");
+
+  char c = ' ';
+  while(in.good() && !in.eof()) {
+    while(isspace(c) && !in.eof())
+      in.get(c);
+
+    if(c == '#') { // skip comments
+      while(c != '\n' && !in.eof())
+        in.get(c);
+      continue;
+    }
+
+    std::string alias;
+    while(!isspace(c) && !in.eof()) {
+      alias += c;
+      in.get(c);
+    }
+    while(isspace(c) && !in.eof())
+      in.get(c);
+    std::string language;
+    while(!isspace(c) && !in.eof()) {
+      language += c;
+      in.get(c);
+    }
+
+    if(in.eof())
+      break;
+    set_language_alias(alias, language);
+  }
+}
+
+Dictionary&
+DictionaryManager::get_dictionary(const std::string& spec)
+{
+
+  //log_debug << "Dictionary for language \"" << spec << "\" requested" << std::endl;
+
+  std::string lang = get_language_from_spec(spec);
+
+  //log_debug << "...normalized as \"" << lang << "\"" << std::endl;
+
+  Dictionaries::iterator i = dictionaries.find(get_language_from_spec(lang));
+  if (i != dictionaries.end())
+    {
+      return i->second;
+    }
+  else // Dictionary for languages lang isn't loaded, so we load it
+    {
+      //log_debug << "get_dictionary: " << lang << std::endl;
+      Dictionary& dict = dictionaries[lang];
+
+      dict.set_language(get_language_def(lang));
+      if(charset != "")
+        dict.set_charset(charset);
+
+      for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
+        {
+          char** files = PHYSFS_enumerateFiles(p->c_str());
+          if(!files)
+            {
+              log_warning << "Error: enumerateFiles() failed on " << *p << std::endl;
+            }
+          else
+            {
+              for(const char* const* filename = files;
+                      *filename != 0; filename++) {
+
+                // check if filename matches requested language
+               std::string fname = std::string(*filename);
+               std::string load_from_file = "";
+                if(fname == lang + ".po") {
+                 load_from_file = fname;
+               } else {
+                  std::string::size_type s = lang.find("_");
+                  if(s != std::string::npos) {
+                    std::string lang_short = std::string(lang, 0, s);
+                   if (fname == lang_short + ".po") {
+                     load_from_file = lang_short;
+                   }
+                  }
+               }
+
+               // if it matched, load dictionary
+               if (load_from_file != "") {
+                  //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
+                  std::string pofile = *p + "/" + *filename;
+                  try {
+                      IFileStream in(pofile);
+                      read_po_file(dict, in);
+                  } catch(std::exception& e) {
+                      log_warning << "Error: Failure file opening: " << pofile << std::endl;
+                      log_warning << e.what() << "" << std::endl;
+                  }
+                }
+
+              }
+              PHYSFS_freeList(files);
+            }
+        }
+
+      return dict;
+    }
+}
+
+std::set<std::string>
+DictionaryManager::get_languages()
+{
+  std::set<std::string> languages;
+
+  for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
+    {
+      char** files = PHYSFS_enumerateFiles(p->c_str());
+      if (!files)
+        {
+          log_warning << "Error: opendir() failed on " << *p << std::endl;
+        }
+      else
+        {
+          for(const char* const* file = files; *file != 0; file++) {
+              if(has_suffix(*file, ".po")) {
+                  std::string filename = *file;
+                  languages.insert(filename.substr(0, filename.length()-3));
+              }
+          }
+          PHYSFS_freeList(files);
+        }
+    }
+  return languages;
+}
+
+void
+DictionaryManager::set_language(const std::string& lang)
+{
+  //log_debug << "set_language \"" << lang << "\"" << std::endl;
+  language = get_language_from_spec(lang);
+  //log_debug << "==> \"" << language << "\"" << std::endl;
+  current_dict = & (get_dictionary(language));
+}
+
+const std::string&
+DictionaryManager::get_language() const
+{
+  return language;
+}
+
+void
+DictionaryManager::set_charset(const std::string& charset)
+{
+  dictionaries.clear(); // changing charset invalidates cache
+  this->charset = charset;
+  set_language(language);
+}
+
+void
+DictionaryManager::set_language_alias(const std::string& alias,
+    const std::string& language)
+{
+  language_aliases.insert(std::make_pair(alias, language));
+}
+
+std::string
+DictionaryManager::get_language_from_spec(const std::string& spec)
+{
+  std::string lang = spec;
+  Aliases::iterator i = language_aliases.find(lang);
+  if(i != language_aliases.end()) {
+    lang = i->second;
+  }
+
+  std::string::size_type s = lang.find(".");
+  if(s != std::string::npos) {
+    lang = std::string(lang, 0, s);
+  }
+
+  s = lang.find("_");
+  if(s == std::string::npos) {
+    std::string lang_big = lang;
+    std::transform (lang_big.begin(), lang_big.end(), lang_big.begin(), toupper);
+    lang += "_" + lang_big;
+  }
+
+  return lang;
+
+}
+
+void
+DictionaryManager::add_directory(const std::string& pathname)
+{
+  dictionaries.clear(); // adding directories invalidates cache
+  search_path.push_back(pathname);
+  set_language(language);
+}
+
+//---------------------------------------------------------------------------
+
+Dictionary::Dictionary(const LanguageDef& language_, const std::string& charset_)
+  : language(language_), charset(charset_)
+{
+}
+
+Dictionary::Dictionary()
+  : language(lang_en)
+{
+}
+
+std::string
+Dictionary::get_charset() const
+{
+  return charset;
+}
+
+void
+Dictionary::set_charset(const std::string& charset_)
+{
+  charset = charset_;
+}
+
+void
+Dictionary::set_language(const LanguageDef& lang)
+{
+  language = lang;
+}
+
+std::string
+Dictionary::translate(const std::string& msgid, const std::string& msgid2, int num)
+{
+  PluralEntries::iterator i = plural_entries.find(msgid);
+  std::map<int, std::string>& msgstrs = i->second;
+
+  if (i != plural_entries.end() && !msgstrs.empty())
+    {
+      int g = language.plural(num);
+      std::map<int, std::string>::iterator j = msgstrs.find(g);
+      if (j != msgstrs.end())
+        {
+          return j->second;
+        }
+      else
+        {
+          // Return the first translation, in case we can't translate the specific number
+          return msgstrs.begin()->second;
+        }
+    }
+  else
+    {
+#ifdef TRANSLATION_DEBUG
+      log_warning << "Couldn't translate: " << msgid << std::endl;
+      log_warning << "Candidates: " << std::endl;
+      for (PluralEntries::iterator i = plural_entries.begin(); i != plural_entries.end(); ++i)
+        log_debug << "'" << i->first << "'" << std::endl;
+#endif
+
+      if (plural2_1(num)) // default to english rules
+        return msgid2;
+      else
+        return msgid;
+    }
+}
+
+const char*
+Dictionary::translate(const char* msgid)
+{
+  Entries::iterator i = entries.find(msgid);
+  if (i != entries.end() && !i->second.empty())
+    {
+      return i->second.c_str();
+    }
+  else
+    {
+#ifdef TRANSLATION_DBEUG
+      log_warning << "Couldn't translate: " << msgid << std::endl;
+#endif
+      return msgid;
+    }
+}
+
+std::string
+Dictionary::translate(const std::string& msgid)
+{
+  Entries::iterator i = entries.find(msgid);
+  if (i != entries.end() && !i->second.empty())
+    {
+      return i->second;
+    }
+  else
+    {
+#ifdef TRANSLATION_DBEUG
+      log_warning << "Couldn't translate: " << msgid << std::endl;
+#endif
+      return msgid;
+    }
+}
+
+void
+Dictionary::add_translation(const std::string& msgid, const std::string& ,
+                            const std::map<int, std::string>& msgstrs)
+{
+  // Do we need msgid2 for anything? its after all supplied to the
+  // translate call, so we just throw it away
+  plural_entries[msgid] = msgstrs;
+}
+
+void
+Dictionary::add_translation(const std::string& msgid, const std::string& msgstr)
+{
+  entries[msgid] = msgstr;
+}
+
+class POFileReader
+{
+private:
+  struct Token
+  {
+    std::string keyword;
+    std::string content;
+  };
+
+  Dictionary& dict;
+
+  std::string from_charset;
+  std::string to_charset;
+
+  std::string current_msgid;
+  std::string current_msgid_plural;
+  std::map<int, std::string> msgstr_plural;
+
+  int line_num;
+
+  enum { WANT_MSGID, WANT_MSGSTR, WANT_MSGSTR_PLURAL, WANT_MSGID_PLURAL } state;
+
+public:
+  POFileReader(std::istream& in, Dictionary& dict_)
+    : dict(dict_)
+  {
+    state = WANT_MSGID;
+    line_num = 0;
+    char c = in.get();
+    if(c == (char) 0xef) { // skip UTF-8 intro that some texteditors produce
+        in.get();
+        in.get();
+    } else {
+        in.unget();
+    }
+    tokenize_po(in);
+  }
+
+  void parse_header(const std::string& header)
+  {
+    // Seperate the header in lines
+    typedef std::vector<std::string> Lines;
+    Lines lines;
+
+    std::string::size_type start = 0;
+    for(std::string::size_type i = 0; i < header.length(); ++i)
+      {
+        if (header[i] == '\n')
+          {
+            lines.push_back(header.substr(start, i - start));
+            start = i+1;
+          }
+      }
+
+    for(Lines::iterator i = lines.begin(); i != lines.end(); ++i)
+      {
+        if (has_prefix(*i, "Content-Type: text/plain; charset=")) {
+          from_charset = i->substr(strlen("Content-Type: text/plain; charset="));
+        }
+      }
+
+    if (from_charset.empty() || from_charset == "CHARSET")
+      {
+        log_warning << "Error: Charset not specified for .po, fallback to ISO-8859-1" << std::endl;
+        from_charset = "ISO-8859-1";
+      }
+
+    to_charset = dict.get_charset();
+    if (to_charset.empty())
+      { // No charset requested from the dict, use utf-8
+        to_charset = "utf-8";
+        dict.set_charset(from_charset);
+      }
+  }
+
+  void add_token(const Token& token)
+  {
+    switch(state)
+      {
+      case WANT_MSGID:
+        if (token.keyword == "msgid")
+          {
+            current_msgid = token.content;
+            state = WANT_MSGID_PLURAL;
+          }
+        else if (token.keyword.empty())
+          {
+            //log_warning << "Got EOF, everything looks ok." << std::endl;
+          }
+        else
+          {
+            log_warning << "tinygettext: expected 'msgid' keyword, got " << token.keyword << " at line " << line_num << std::endl;
+          }
+        break;
+
+      case WANT_MSGID_PLURAL:
+        if (token.keyword == "msgid_plural")
+          {
+            current_msgid_plural = token.content;
+            state = WANT_MSGSTR_PLURAL;
+          }
+        else
+          {
+            state = WANT_MSGSTR;
+            add_token(token);
+          }
+        break;
+
+      case WANT_MSGSTR:
+        if (token.keyword == "msgstr")
+          {
+            if (current_msgid == "")
+              { // .po Header is hidden in the msgid with the empty string
+                parse_header(token.content);
+              }
+            else
+              {
+                dict.add_translation(current_msgid, convert(token.content, from_charset, to_charset));
+              }
+            state = WANT_MSGID;
+          }
+        else
+          {
+            log_warning << "tinygettext: expected 'msgstr' keyword, got " << token.keyword << " at line " << line_num << std::endl;
+          }
+        break;
+
+      case WANT_MSGSTR_PLURAL:
+        if (has_prefix(token.keyword, "msgstr["))
+          {
+            int num;
+            if (sscanf(token.keyword.c_str(), "msgstr[%d]", &num) != 1)
+              {
+                log_warning << "Error: Couldn't parse: " << token.keyword << std::endl;
+              }
+            else
+              {
+                msgstr_plural[num] = convert(token.content, from_charset, to_charset);
+              }
+          }
+        else
+          {
+            dict.add_translation(current_msgid, current_msgid_plural, msgstr_plural);
+
+            state = WANT_MSGID;
+            add_token(token);
+          }
+        break;
+      }
+  }
+
+  inline int getchar(std::istream& in)
+  {
+    int c = in.get();
+    if (c == '\n')
+      line_num += 1;
+    return c;
+  }
+
+  void tokenize_po(std::istream& in)
+  {
+    enum State { READ_KEYWORD,
+                 READ_CONTENT,
+                 READ_CONTENT_IN_STRING,
+                 SKIP_COMMENT };
+
+    State state = READ_KEYWORD;
+    int c;
+    Token token;
+
+    while((c = getchar(in)) != EOF)
+      {
+        //log_debug << "Lexing char: " << char(c) << " " << state << std::endl;
+        switch(state)
+          {
+          case READ_KEYWORD:
+            if (c == '#')
+              {
+                state = SKIP_COMMENT;
+              }
+            else
+              {
+                // Read a new token
+                token = Token();
+
+                do { // Read keyword
+                  token.keyword += c;
+                } while((c = getchar(in)) != EOF && !isspace(c));
+                in.unget();
+
+                state = READ_CONTENT;
+              }
+            break;
+
+          case READ_CONTENT:
+            while((c = getchar(in)) != EOF)
+              {
+                if (c == '"') {
+                  // Found start of content
+                  state = READ_CONTENT_IN_STRING;
+                  break;
+                } else if (isspace(c)) {
+                  // skip
+                } else { // Read something that may be a keyword
+                  in.unget();
+                  state = READ_KEYWORD;
+                  add_token(token);
+                  break;
+                }
+              }
+            break;
+
+          case READ_CONTENT_IN_STRING:
+            if (c == '\\') {
+              c = getchar(in);
+              if (c != EOF)
+                {
+                  if (c == 'n') token.content += '\n';
+                  else if (c == 't') token.content += '\t';
+                  else if (c == 'r') token.content += '\r';
+                  else if (c == '"') token.content += '"';
+                  else if (c == '\\') token.content += '\\';
+                  else
+                    {
+                      log_warning << "Unhandled escape character: " << char(c) << std::endl;
+                    }
+                }
+              else
+                {
+                  log_warning << "Unterminated string" << std::endl;
+                }
+            } else if (c == '"') { // Content string is terminated
+              state = READ_CONTENT;
+            } else {
+              token.content += c;
+            }
+            break;
+
+          case SKIP_COMMENT:
+            if (c == '\n')
+              state = READ_KEYWORD;
+            break;
+          }
+      }
+    add_token(token);
+  }
+};
+
+void read_po_file(Dictionary& dict_, std::istream& in)
+{
+  POFileReader reader(in, dict_);
+}
+
+} // namespace TinyGetText
+
+/* EOF */
diff --git a/src/tinygettext/tinygettext.hpp b/src/tinygettext/tinygettext.hpp
new file mode 100644 (file)
index 0000000..fe0dff0
--- /dev/null
@@ -0,0 +1,159 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef HEADER_TINYGETTEXT_H
+#define HEADER_TINYGETTEXT_H
+
+#include <map>
+#include <vector>
+#include <set>
+#include <string>
+
+namespace TinyGetText {
+
+typedef int (*PluralFunc)(int n);
+
+struct LanguageDef {
+  const char* code;
+  const char* name;
+  int         nplural;
+  PluralFunc  plural;
+
+  LanguageDef(const char* code_, const char* name_,  int nplural_, PluralFunc plural_)
+    : code(code_), name(name_), nplural(nplural_), plural(plural_)
+  {}
+};
+
+/** A simple dictionary class that mimics gettext() behaviour. Each
+    Dictionary only works for a single language, for managing multiple
+    languages and .po files at once use the DictionaryManager. */
+class Dictionary
+{
+private:
+  typedef std::map<std::string, std::string> Entries;
+  Entries entries;
+
+  typedef std::map<std::string, std::map<int, std::string> > PluralEntries;
+  PluralEntries plural_entries;
+
+  LanguageDef language;
+  std::string charset;
+public:
+  /** */
+  Dictionary(const LanguageDef& language_, const std::string& charset = "");
+
+  Dictionary();
+
+  /** Return the charset used for this dictionary */
+  std::string get_charset() const;
+
+  /** Set a charset for this dictionary, this will NOT convert stuff,
+      it is for information only, you have to convert stuff yourself
+      when you add it with \a add_translation() */
+  void set_charset(const std::string& charset);
+
+  /** Set the language that is used for this dictionary, this is
+      mainly needed to evaluate plural forms */
+  void set_language(const LanguageDef& lang);
+
+  /** Translate the string \a msgid to its correct plural form, based
+      on the number of items given by \a num. \a msgid2 is \a msgid in
+      plural form. */
+  std::string translate(const std::string& msgid, const std::string& msgid2, int num);
+
+  /** Translate the string \a msgid. */
+  std::string translate(const std::string& msgid);
+  /** Translate the string \a msgid. */
+  const char* translate(const char* msgid);
+
+  /** Add a translation from \a msgid to \a msgstr to the dictionary,
+      where \a msgid is the singular form of the message, msgid2 the
+      plural form and msgstrs a table of translations. The right
+      translation will be calculated based on the \a num argument to
+      translate(). */
+  void add_translation(const std::string& msgid, const std::string& msgid2,
+                       const std::map<int, std::string>& msgstrs);
+
+  /** Add a translation from \a msgid to \a msgstr to the
+      dictionary */
+  void add_translation(const std::string& msgid, const std::string& msgstr);
+};
+
+/** Manager class for dictionaries, you give it a bunch of directories
+    with .po files and it will then automatically load the right file
+    on demand depending on which language was set. */
+class DictionaryManager
+{
+private:
+  typedef std::map<std::string, Dictionary> Dictionaries;
+  Dictionaries dictionaries;
+  typedef std::vector<std::string> SearchPath;
+  SearchPath search_path;
+  typedef std::map<std::string, std::string> Aliases;
+  Aliases language_aliases;
+  std::string charset;
+  std::string language;
+  Dictionary* current_dict;
+  Dictionary empty_dict;
+
+public:
+  DictionaryManager();
+
+  /** Return the currently active dictionary, if none is set, an empty
+      dictionary is returned. */
+  Dictionary& get_dictionary()
+  { return *current_dict; }
+
+  /** Get dictionary for lang */
+  Dictionary& get_dictionary(const std::string& langspec);
+
+  /** Set a language based on a four? letter country code */
+  void set_language(const std::string& langspec);
+
+  /** returns the (normalized) country code of the currently used language */
+  const std::string& get_language() const;
+
+  /** Set a charset that will be set on the returned dictionaries */
+  void set_charset(const std::string& charset);
+
+  /** Define an alias for a language */
+  void set_language_alias(const std::string& alias, const std::string& lang);
+
+  /** Add a directory to the search path for dictionaries */
+  void add_directory(const std::string& pathname);
+
+  /** Return a set of the available languages in their country code */
+  std::set<std::string> get_languages();
+
+private:
+  void parseLocaleAliases();
+  /// returns the language part in a language spec (like de_DE.UTF-8 -> de)
+  std::string get_language_from_spec(const std::string& spec);
+};
+
+/** Read the content of the .po file given as \a in into the
+    dictionary given as \a dict */
+void read_po_file(Dictionary& dict, std::istream& in);
+LanguageDef& get_language_def(const std::string& name);
+
+} // namespace TinyGetText
+
+#endif
+
+/* EOF */
diff --git a/src/title.cpp b/src/title.cpp
new file mode 100644 (file)
index 0000000..a53f8ab
--- /dev/null
@@ -0,0 +1,581 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <SDL.h>
+#include <SDL_image.h>
+#include <physfs.h>
+
+#include "title.hpp"
+#include "mainloop.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "audio/sound_manager.hpp"
+#include "gui/menu.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "level.hpp"
+#include "world.hpp"
+#include "game_session.hpp"
+#include "worldmap/worldmap.hpp"
+#include "player_status.hpp"
+#include "tile.hpp"
+#include "sector.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/player.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "textscroller.hpp"
+#include "fadeout.hpp"
+#include "file_system.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "control/codecontroller.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "options_menu.hpp"
+#include "console.hpp"
+#include "random_generator.hpp"
+#include "addon_manager.hpp"
+
+enum MainMenuIDs {
+  MNID_STARTGAME,
+  MNID_LEVELS_CONTRIB,
+  MNID_ADDONS,
+  MNID_OPTIONMENU,
+  MNID_LEVELEDITOR,
+  MNID_CREDITS,
+  MNID_QUITMAINMENU
+};
+
+void
+TitleScreen::update_load_game_menu()
+{
+  load_game_menu.reset(new Menu());
+
+  load_game_menu->add_label(_("Start Game"));
+  load_game_menu->add_hl();
+  for(int i = 1; i <= 5; ++i) {
+    load_game_menu->add_entry(i, get_slotinfo(i));
+  }
+  load_game_menu->add_hl();
+  load_game_menu->add_back(_("Back"));
+}
+
+void
+TitleScreen::free_contrib_menu()
+{
+  for(std::vector<World*>::iterator i = contrib_worlds.begin();
+      i != contrib_worlds.end(); ++i)
+    delete *i;
+
+  contrib_worlds.clear();
+}
+
+void
+TitleScreen::generate_contrib_menu()
+{
+  /** Generating contrib levels list by making use of Level Subset  */
+  std::vector<std::string> level_worlds;
+  char** files = PHYSFS_enumerateFiles("levels/");
+  for(const char* const* filename = files; *filename != 0; ++filename) {
+    std::string filepath = std::string("levels/") + *filename;
+    if(PHYSFS_isDirectory(filepath.c_str()))
+      level_worlds.push_back(filepath);
+  }
+  PHYSFS_freeList(files);
+
+  free_contrib_menu();
+  contrib_menu.reset(new Menu());
+
+  contrib_menu->add_label(_("Contrib Levels"));
+  contrib_menu->add_hl();
+
+  int i = 0;
+  for (std::vector<std::string>::iterator it = level_worlds.begin();
+      it != level_worlds.end(); ++it) {
+    try {
+      std::auto_ptr<World> world (new World());
+      world->load(*it + "/info");
+      if(world->hide_from_contribs) {
+        continue;
+      }
+      contrib_menu->add_entry(i++, world->title);
+      contrib_worlds.push_back(world.release());
+    } catch(std::exception& e) {
+#ifdef DEBUG
+      log_warning << "Couldn't parse levelset info for '" << *it << "': " << e.what() << std::endl;
+#endif
+    }
+  }
+
+  contrib_menu->add_hl();
+  contrib_menu->add_back(_("Back"));
+}
+
+std::string
+TitleScreen::get_level_name(const std::string& filename)
+{
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(filename);
+
+    const lisp::Lisp* level = root->get_lisp("supertux-level");
+    if(!level)
+      return "";
+
+    std::string name;
+    level->get("name", name);
+    return name;
+  } catch(std::exception& e) {
+         log_warning << "Problem getting name of '" << filename << "': "
+                  << e.what() << std::endl;
+    return "";
+  }
+}
+
+void
+TitleScreen::check_levels_contrib_menu()
+{
+  int index = contrib_menu->check();
+  if (index == -1)
+    return;
+
+  current_world = contrib_worlds[index];
+
+  if(!current_world->is_levelset) {
+    update_load_game_menu();
+    Menu::push_current(load_game_menu.get());
+  } else {
+    contrib_world_menu.reset(new Menu());
+
+    contrib_world_menu->add_label(current_world->title);
+    contrib_world_menu->add_hl();
+
+    for (unsigned int i = 0; i < current_world->get_num_levels(); ++i)
+    {
+      /** get level's title */
+      std::string filename = current_world->get_level_filename(i);
+      std::string title = get_level_name(filename);
+      contrib_world_menu->add_entry(i, title);
+    }
+
+    contrib_world_menu->add_hl();
+    contrib_world_menu->add_back(_("Back"));
+
+    Menu::push_current(contrib_world_menu.get());
+  }
+}
+
+void
+TitleScreen::check_contrib_world_menu()
+{
+  int index = contrib_world_menu->check();
+  if (index != -1) {
+    if (contrib_world_menu->get_item_by_id(index).kind == MN_ACTION) {
+      sound_manager->stop_music();
+      GameSession* session =
+        new GameSession(current_world->get_level_filename(index));
+      main_loop->push_screen(session);
+    }
+  }
+}
+
+namespace {
+  bool generate_addons_menu_sorter(const Addon& a1, const Addon& a2)
+  {
+    return a1.title < a2.title;
+  }
+
+  const int ADDON_LIST_START_ID = 10;
+}
+
+void
+TitleScreen::generate_addons_menu()
+{
+  AddonManager& adm = AddonManager::get_instance();
+
+  // refresh list of installed addons
+  installed_addons = adm.get_installed_addons();
+  
+  // build new Add-on list
+  addons.clear();
+
+  // add installed addons to list
+  addons.insert(addons.end(), installed_addons.begin(), installed_addons.end());
+
+  // add available addons to list
+  addons.insert(addons.end(), available_addons.begin(), available_addons.end());
+
+  // sort list
+  std::sort(addons.begin(), addons.end(), generate_addons_menu_sorter);
+
+  // remove available addons that are already installed
+  std::vector<Addon>::iterator it2 = addons.begin();
+  while (it2 != addons.end()) {
+    Addon addon = *it2;
+    if (addon.isInstalled) {
+      bool restart = false;
+      for (std::vector<Addon>::iterator it = addons.begin(); it != addons.end(); ++it) {
+        Addon addon2 = *it;
+        if ((addon2.equals(addon)) && (!addon2.isInstalled)) {
+          addons.erase(it);
+          restart = true;
+          break;
+        }
+      }
+      if (restart) {
+        it2 = addons.begin();
+        continue;
+      }
+    }
+    it2++;
+  }
+
+  // (re)generate menu
+  free_addons_menu();
+  addons_menu.reset(new Menu());
+
+  addons_menu->add_label(_("Add-ons"));
+  addons_menu->add_hl();
+  
+#ifdef HAVE_LIBCURL
+  addons_menu->add_entry(0, std::string(_("Check Online")));
+#else
+  addons_menu->add_deactive(0, std::string(_("Check Online (disabled)")));
+#endif
+
+  //addons_menu->add_hl();
+
+  for (unsigned int i = 0; i < addons.size(); i++) {
+    Addon addon = addons[i];
+    std::string text = "";
+    if (addon.kind != "") text += addon.kind + " ";
+    text += std::string("\"") + addon.title + "\"";
+    if (addon.author != "") text += " by \"" + addon.author + "\"";
+    addons_menu->add_toggle(ADDON_LIST_START_ID + i, text, addon.isInstalled);
+  }
+
+  addons_menu->add_hl();
+  addons_menu->add_back(_("Back"));
+}
+
+void
+TitleScreen::check_addons_menu()
+{
+  int index = addons_menu->check();
+  if (index == -1) return;
+
+  // check if "Check Online" was chosen
+  if (index == 0) {
+    try {
+      available_addons = AddonManager::get_instance().get_available_addons();
+      generate_addons_menu();
+      Menu::set_current(addons_menu.get());
+      addons_menu->set_active_item(index);
+    } 
+    catch (std::runtime_error e) {
+      log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
+    }
+    return;
+  }
+
+  // if one of the Addons listed was chosen, take appropriate action
+  if ((index >= ADDON_LIST_START_ID) && (index < ADDON_LIST_START_ID) + addons.size()) {
+    Addon addon = addons[index - ADDON_LIST_START_ID];
+    if (!addon.isInstalled) {
+      try {
+        addon.install();
+        //generate_addons_menu();
+        //Menu::set_current(addons_menu.get());
+        //addons_menu->set_active_item(index);
+        Menu::set_current(0);
+      } 
+      catch (std::runtime_error e) {
+        log_warning << "Installation of Add-on failed: " << e.what() << std::endl;
+      }
+    } else {
+      try {
+        addon.remove();
+        //generate_addons_menu();
+        //Menu::set_current(addons_menu.get());
+        //addons_menu->set_active_item(index);
+        Menu::set_current(0);
+      } 
+      catch (std::runtime_error e) {
+        log_warning << "Removal of Add-on failed: " << e.what() << std::endl;
+      }
+    }
+  }
+
+}
+
+void
+TitleScreen::free_addons_menu()
+{
+}
+
+void
+TitleScreen::make_tux_jump()
+{
+  static bool jumpWasReleased = true;
+  Sector* sector  = titlesession->get_current_sector();
+  Player* tux = sector->player;
+
+  controller->update();
+  controller->press(Controller::RIGHT);
+
+  // Check if we should press the jump button
+  Rect lookahead = tux->get_bbox();
+  lookahead.p2.x += 96;
+  bool pathBlocked = !sector->is_free_of_statics(lookahead);
+  if ((pathBlocked && jumpWasReleased) || !tux->on_ground()) {
+    controller->press(Controller::JUMP);
+    jumpWasReleased = false;
+  } else {
+    jumpWasReleased = true;
+  }
+
+  // Wrap around at the end of the level back to the beginnig
+  if(sector->get_width() - 320 < tux->get_pos().x) {
+    sector->activate("main");
+    sector->camera->reset(tux->get_pos());
+  }
+}
+
+TitleScreen::TitleScreen()
+{
+  controller.reset(new CodeController());
+  titlesession.reset(new GameSession("levels/misc/menu.stl"));
+
+  Player* player = titlesession->get_current_sector()->player;
+  player->set_controller(controller.get());
+  player->set_speedlimit(230); //MAX_WALK_XM
+
+  generate_main_menu();
+}
+
+void
+TitleScreen::generate_main_menu()
+{
+  main_menu.reset(new Menu());
+  main_menu->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 35);
+  main_menu->add_entry(MNID_STARTGAME, _("Start Game"));
+  main_menu->add_entry(MNID_LEVELS_CONTRIB, _("Contrib Levels"));
+  main_menu->add_entry(MNID_ADDONS, _("Add-ons"));
+  main_menu->add_submenu(_("Options"), get_options_menu());
+  main_menu->add_entry(MNID_CREDITS, _("Credits"));
+  main_menu->add_entry(MNID_QUITMAINMENU, _("Quit"));
+}
+
+TitleScreen::~TitleScreen()
+{
+}
+
+void
+TitleScreen::setup()
+{
+  player_status->reset();
+
+  Sector* sector = titlesession->get_current_sector();
+  if(Sector::current() != sector) {
+    sector->play_music(LEVEL_MUSIC);
+    sector->activate(sector->player->get_pos());
+  }
+
+  Menu::set_current(main_menu.get());
+}
+
+void
+TitleScreen::leave()
+{
+  Sector* sector = titlesession->get_current_sector();
+  sector->deactivate();
+  Menu::set_current(NULL);
+}
+
+void
+TitleScreen::draw(DrawingContext& context)
+{
+  Sector* sector  = titlesession->get_current_sector();
+  sector->draw(context);
+
+  context.draw_text(white_small_text, "SuperTux " PACKAGE_VERSION "\n",
+      Vector(5, SCREEN_HEIGHT - 50), ALIGN_LEFT, LAYER_FOREGROUND1);
+  context.draw_text(white_small_text,
+      _(
+"Copyright (c) 2007 SuperTux Devel Team\n"
+"This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to\n"
+"redistribute it under certain conditions; see the file COPYING for details.\n"
+),
+      Vector(5, SCREEN_HEIGHT - 50 + white_small_text->get_height() + 5),
+      ALIGN_LEFT, LAYER_FOREGROUND1);
+}
+
+void
+TitleScreen::update(float elapsed_time)
+{
+  main_loop->set_speed(0.6f);
+  Sector* sector  = titlesession->get_current_sector();
+  sector->update(elapsed_time);
+
+  make_tux_jump();
+
+  Menu* menu = Menu::current();
+  if(menu) {
+    menu->update();
+
+    if(menu == main_menu.get()) {
+      switch (main_menu->check()) {
+        case MNID_STARTGAME:
+          // Start Game, ie. goto the slots menu
+          if(main_world.get() == NULL) {
+            main_world.reset(new World());
+            main_world->load("levels/world1/info");
+          }
+          current_world = main_world.get();
+          update_load_game_menu();
+          Menu::push_current(load_game_menu.get());
+          break;
+        case MNID_LEVELS_CONTRIB:
+          // Contrib Menu
+          generate_contrib_menu();
+          Menu::push_current(contrib_menu.get());
+          break;
+        case MNID_ADDONS:
+          // Add-ons Menu
+          generate_addons_menu();
+          Menu::push_current(addons_menu.get());
+          break;
+        case MNID_CREDITS:
+          main_loop->push_screen(new TextScroller("credits.txt"),
+                                 new FadeOut(0.5));
+          break;
+        case MNID_QUITMAINMENU:
+          main_loop->quit(new FadeOut(0.25));
+                 sound_manager->stop_music(0.25);
+          break;
+      }
+    } else if(menu == load_game_menu.get()) {
+      /*
+      if(event.key.keysym.sym == SDLK_DELETE) {
+        int slot = menu->get_active_item_id();
+        std::stringstream stream;
+        stream << slot;
+        std::string str = _("Are you sure you want to delete slot") + stream.str() + "?";
+
+        if(confirm_dialog(bkg_title, str.c_str())) {
+          str = "save/slot" + stream.str() + ".stsg";
+          log_debug << "Removing: " << str << std::endl;
+          PHYSFS_delete(str.c_str());
+        }
+
+        update_load_save_game_menu(load_game_menu);
+        Menu::set_current(main_menu.get());
+      }*/
+      process_load_game_menu();
+    } else if(menu == contrib_menu.get()) {
+      check_levels_contrib_menu();
+    } else if(menu == addons_menu.get()) {
+      check_addons_menu();
+    } else if (menu == contrib_world_menu.get()) {
+      check_contrib_world_menu();
+    }
+  }
+
+  // reopen menu of user closed it (so that the app doesn't close when user
+  // accidently hit ESC)
+  if(Menu::current() == 0) {
+    generate_main_menu();
+    Menu::set_current(main_menu.get());
+  }
+}
+
+std::string
+TitleScreen::get_slotinfo(int slot)
+{
+  std::string tmp;
+  std::string title;
+
+  std::string basename = current_world->get_basedir();
+  basename = basename.substr(0, basename.length()-1);
+  std::string worlddirname = FileSystem::basename(basename);
+  std::ostringstream stream;
+  stream << "save/" << worlddirname << "_" << slot << ".stsg";
+  std::string slotfile = stream.str();
+
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(slotfile);
+
+    const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
+    if(!savegame)
+      throw std::runtime_error("file is not a supertux-savegame.");
+
+    savegame->get("title", title);
+  } catch(std::exception& ) {
+    std::ostringstream slottitle;
+    slottitle << _("Slot") << " " << slot << " - " << _("Free");
+    return slottitle.str();
+  }
+
+  std::ostringstream slottitle;
+  slottitle << _("Slot") << " " << slot << " - " << title;
+  return slottitle.str();
+}
+
+bool
+TitleScreen::process_load_game_menu()
+{
+  int slot = load_game_menu->check();
+
+  if(slot == -1)
+    return false;
+
+  if(load_game_menu->get_item_by_id(slot).kind != MN_ACTION)
+    return false;
+
+  std::string basename = current_world->get_basedir();
+  basename = basename.substr(0, basename.length()-1);
+  std::string worlddirname = FileSystem::basename(basename);
+  std::stringstream stream;
+  stream << "save/" << worlddirname << "_" << slot << ".stsg";
+  std::string slotfile = stream.str();
+
+  try {
+    current_world->set_savegame_filename(slotfile);
+    current_world->run();
+  } catch(std::exception& e) {
+    log_fatal << "Couldn't start world: " << e.what() << std::endl;
+  }
+
+  return true;
+}
diff --git a/src/title.hpp b/src/title.hpp
new file mode 100644 (file)
index 0000000..466ec0e
--- /dev/null
@@ -0,0 +1,78 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef SUPERTUX_TITLE_H
+#define SUPERTUX_TITLE_H
+
+#include <memory>
+#include <vector>
+#include "screen.hpp"
+#include "game_session.hpp"
+#include "addon.hpp"
+
+class Menu;
+class World;
+class CodeController;
+
+class TitleScreen : public Screen
+{
+public:
+  TitleScreen();
+  virtual ~TitleScreen();
+
+  virtual void setup();
+  virtual void leave();
+
+  virtual void draw(DrawingContext& context);
+
+  virtual void update(float elapsed_time);
+
+private:
+  std::string get_slotinfo(int slot);
+  std::string get_level_name(const std::string& levelfile);
+  bool process_load_game_menu();
+  void make_tux_jump();
+  void update_load_game_menu();
+  void generate_main_menu();
+  void generate_contrib_menu();
+  void check_levels_contrib_menu();
+  void check_contrib_world_menu();
+  void free_contrib_menu();
+  void generate_addons_menu();
+  void check_addons_menu();
+  void free_addons_menu();
+
+  std::auto_ptr<Menu> main_menu;
+  std::auto_ptr<Menu> load_game_menu;
+  std::auto_ptr<Menu> contrib_menu;
+  std::auto_ptr<Menu> contrib_world_menu;
+  std::auto_ptr<World> main_world;
+  std::vector<World*> contrib_worlds;
+  std::auto_ptr<Menu> addons_menu;
+  std::vector<Addon> addons; /**< shown list of Add-ons */
+  std::vector<Addon> available_addons; /**< list of downloadable Add-ons */
+  std::vector<Addon> installed_addons; /**< list of currently installed Add-ons */
+  World* current_world;
+
+  std::auto_ptr<CodeController> controller;
+  std::auto_ptr<GameSession> titlesession;
+};
+
+#endif
diff --git a/src/trigger/climbable.cpp b/src/trigger/climbable.cpp
new file mode 100644 (file)
index 0000000..b2a4e49
--- /dev/null
@@ -0,0 +1,135 @@
+//  $Id$
+//
+//  SuperTux - Climbable area
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "climbable.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "gettext.hpp"
+#include "object/tilemap.hpp"
+
+namespace {
+  const float GRACE_DX = 8; // how far off may the player's bounding-box be x-wise
+  const float GRACE_DY = 8; // how far off may the player's bounding-box be y-wise
+  const float ACTIVATE_TRY_FOR = 1; // how long to try correcting mis-alignment of player and climbable before giving up
+  const float POSITION_FIX_AX = 50; // x-wise acceleration applied to player when trying to align player and Climbable
+  const float POSITION_FIX_AY = 50; // y-wise acceleration applied to player when trying to align player and Climbable
+}
+
+Climbable::Climbable(const lisp::Lisp& reader)
+  : climbed_by(0)
+{
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  float w = 32, h = 32;
+  reader.get("width", w);
+  reader.get("height", h);
+  bbox.set_size(w, h);
+}
+
+Climbable::Climbable(const Rect& area)
+  : climbed_by(0)
+{
+  bbox = area;
+}
+
+Climbable::~Climbable()
+{
+  if (climbed_by) {
+    climbed_by->stop_climbing(*this);
+    climbed_by = 0;
+  }
+}
+
+void
+Climbable::write(lisp::Writer& writer)
+{
+  writer.start_list("climbable");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_float("width", bbox.get_width());
+  writer.write_float("height", bbox.get_height());
+
+  writer.end_list("climbable");
+}
+
+void 
+Climbable::update(float /*elapsed_time*/)
+{
+  if (!climbed_by) return;
+
+  if (!may_climb(*climbed_by)) {
+    climbed_by->stop_climbing(*this);
+    climbed_by = 0;
+  }
+}
+
+void
+Climbable::draw(DrawingContext& context)
+{
+  if (climbed_by) {
+    context.push_transform();
+    context.set_translation(Vector(0, 0));
+    Vector pos = Vector(0, SCREEN_HEIGHT/2 - gold_text->get_height()/2);
+    context.draw_center_text(gold_text, "Up we go...", pos, LAYER_GUI);
+    context.pop_transform();
+  }
+}
+
+void
+Climbable::event(Player& player, EventType type)
+{
+  if ((type == EVENT_ACTIVATE) || (activate_try_timer.started())) {
+    if (may_climb(player)) {
+      climbed_by = &player;
+      player.start_climbing(*this);
+      activate_try_timer.stop();
+    } else {
+      if (type == EVENT_ACTIVATE) activate_try_timer.start(ACTIVATE_TRY_FOR);
+      if (player.get_bbox().p1.x < get_bbox().p1.x - GRACE_DX) player.add_velocity(Vector(POSITION_FIX_AX,0));
+      if (player.get_bbox().p2.x > get_bbox().p2.x + GRACE_DX) player.add_velocity(Vector(-POSITION_FIX_AX,0));
+      if (player.get_bbox().p1.y < get_bbox().p1.y - GRACE_DY) player.add_velocity(Vector(0,POSITION_FIX_AY));
+      if (player.get_bbox().p2.y > get_bbox().p2.y + GRACE_DY) player.add_velocity(Vector(0,-POSITION_FIX_AY));
+    }
+  }
+  if(type == EVENT_LOSETOUCH) {
+    player.stop_climbing(*this);
+    climbed_by = 0;
+  }
+}
+
+bool
+Climbable::may_climb(Player& player) 
+{
+  if (player.get_bbox().p1.x < get_bbox().p1.x - GRACE_DX) return false;
+  if (player.get_bbox().p2.x > get_bbox().p2.x + GRACE_DX) return false;
+  if (player.get_bbox().p1.y < get_bbox().p1.y - GRACE_DY) return false;
+  if (player.get_bbox().p2.y > get_bbox().p2.y + GRACE_DY) return false;
+  return true;
+}
+
+IMPLEMENT_FACTORY(Climbable, "climbable");
+
diff --git a/src/trigger/climbable.hpp b/src/trigger/climbable.hpp
new file mode 100644 (file)
index 0000000..170483d
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux - Climbable area
+//  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __CLIMBABLE_H__
+#define __CLIMBABLE_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "timer.hpp"
+#include "object/player.hpp"
+
+class Climbable : public TriggerBase, public Serializable
+{
+public:
+  Climbable(const lisp::Lisp& reader);
+  Climbable(const Rect& area);
+  ~Climbable();
+
+  void write(lisp::Writer& writer);
+  void event(Player& player, EventType type);
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+
+  /**
+   * returns true if the player is within bounds of the Climbable
+   */
+  bool may_climb(Player& player);
+
+protected:
+  Player* climbed_by; /**< set to player who's currently climbing us, null if nobody is */
+  Timer activate_try_timer; /**< try to correct mis-alignment while this timer runs */
+};
+
+#endif
diff --git a/src/trigger/door.cpp b/src/trigger/door.cpp
new file mode 100644 (file)
index 0000000..f8d2b72
--- /dev/null
@@ -0,0 +1,163 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "door.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "audio/sound_manager.hpp"
+
+Door::Door(const lisp::Lisp& reader)
+       : state(CLOSED)
+{
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  reader.get("sector", target_sector);
+  reader.get("spawnpoint", target_spawnpoint);
+
+  sprite = sprite_manager->create("images/objects/door/door.sprite");
+  sprite->set_action("closed");
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Door::Door(int x, int y, std::string sector, std::string spawnpoint)
+       : state(CLOSED)
+{
+  bbox.set_pos(Vector(x, y));
+  target_sector = sector;
+  target_spawnpoint = spawnpoint;
+
+  sprite = sprite_manager->create("images/objects/door/door.sprite");
+  sprite->set_action("closed");
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Door::~Door()
+{
+  delete sprite;
+}
+
+void
+Door::write(lisp::Writer& writer)
+{
+  writer.start_list("door");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_float("width", bbox.get_width());
+  writer.write_float("height", bbox.get_height());
+
+  writer.write_string("sector", target_sector);
+  writer.write_string("spawnpoint", target_spawnpoint);
+  sound_manager->preload("sounds/door.wav");
+  writer.end_list("door");
+}
+
+void
+Door::update(float )
+{
+  switch (state) {
+    case CLOSED:
+      break;
+    case OPENING:
+      // if door has finished opening, start timer and keep door open
+      if(sprite->animation_done()) {
+       state = OPEN;
+       sprite->set_action("open");
+       stay_open_timer.start(1.0);
+      }
+      break;
+    case OPEN:
+      // if door was open long enough, start closing it
+      if (stay_open_timer.check()) {
+       state = CLOSING;
+       sprite->set_action("closing", 1);
+      }
+      break;
+    case CLOSING:
+      // if door has finished closing, keep it shut
+      if(sprite->animation_done()) {
+       state = CLOSED;
+       sprite->set_action("closed");
+      }
+      break;
+  }
+}
+
+void
+Door::draw(DrawingContext& context)
+{
+  sprite->draw(context, bbox.p1, LAYER_BACKGROUNDTILES+1);
+}
+
+void
+Door::event(Player& , EventType type)
+{
+  switch (state) {
+    case CLOSED:
+      // if door was activated, start opening it
+      if (type == EVENT_ACTIVATE) {
+       state = OPENING;
+        sound_manager->play("sounds/door.wav");
+       sprite->set_action("opening", 1);
+      }
+      break;
+    case OPENING:
+      break;
+    case OPEN:
+      break;
+    case CLOSING:
+      break;
+  }
+}
+
+HitResponse
+Door::collision(GameObject& other, const CollisionHit& hit)
+{
+  switch (state) {
+    case CLOSED:
+      break;
+    case OPENING:
+      break;
+    case OPEN:
+      {
+        // if door is open and was touched by a player, teleport the player
+       Player* player = dynamic_cast<Player*> (&other);
+       if (player) {
+         state = CLOSING;
+         sprite->set_action("closing", 1);
+         GameSession::current()->respawn(target_sector, target_spawnpoint);
+       }
+      }
+      break;
+    case CLOSING:
+      break;
+  }
+
+  return TriggerBase::collision(other, hit);
+}
+
+IMPLEMENT_FACTORY(Door, "door");
diff --git a/src/trigger/door.hpp b/src/trigger/door.hpp
new file mode 100644 (file)
index 0000000..925edd6
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_DOOR_H
+#define SUPERTUX_DOOR_H
+
+#include <string>
+
+#include "video/surface.hpp"
+#include "sprite/sprite.hpp"
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "timer.hpp"
+#include "object/player.hpp"
+
+class Door : public TriggerBase, public Serializable
+{
+public:
+  Door(const lisp::Lisp& reader);
+  Door(int x, int y, std::string sector, std::string spawnpoint);
+  virtual ~Door();
+
+  virtual void write(lisp::Writer& writer);
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+  virtual void event(Player& player, EventType type);
+  virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+  enum DoorState {
+    CLOSED,
+    OPENING,
+    OPEN,
+    CLOSING
+  };
+
+  DoorState state; /**< current state of the door */
+  std::string target_sector; /**< target sector to teleport to */
+  std::string target_spawnpoint; /**< target spawnpoint to teleport to */
+  Sprite* sprite; /**< "door" sprite to render */
+  Timer stay_open_timer; /**< time until door will close again */
+};
+
+#endif
diff --git a/src/trigger/scripttrigger.cpp b/src/trigger/scripttrigger.cpp
new file mode 100644 (file)
index 0000000..6af9b8c
--- /dev/null
@@ -0,0 +1,92 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <memory>
+
+#include "scripttrigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+
+ScriptTrigger::ScriptTrigger(const lisp::Lisp& reader)
+{
+  bool must_activate = false;
+
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  float w = 0, h = 0;
+  reader.get("width", w);
+  reader.get("height", h);
+  bbox.set_size(w, h);
+  reader.get("script", script);
+  reader.get("button", must_activate);
+  if(script == "") {
+    throw std::runtime_error("Need to specify a script for trigger object");
+  }
+
+  if (must_activate)
+    triggerevent = EVENT_ACTIVATE;
+  else
+    triggerevent = EVENT_TOUCH;
+}
+
+ScriptTrigger::ScriptTrigger(const Vector& pos, const std::string& script)
+{
+  bbox.set_pos(pos);
+  bbox.set_size(32, 32);
+  this->script = script;
+  triggerevent = EVENT_TOUCH;
+}
+
+ScriptTrigger::~ScriptTrigger()
+{
+}
+
+void
+ScriptTrigger::write(lisp::Writer& writer)
+{
+  writer.start_list("scripttrigger");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_float("width", bbox.get_width());
+  writer.write_float("height", bbox.get_height());
+  writer.write_string("script", script);
+  writer.write_bool("button", triggerevent == EVENT_ACTIVATE);
+
+  writer.end_list("scripttrigger");
+}
+
+void
+ScriptTrigger::event(Player& , EventType type)
+{
+  if(type != triggerevent)
+    return;
+
+  std::istringstream stream(script);
+  Sector::current()->run_script(stream, "ScriptTrigger");
+}
+
+IMPLEMENT_FACTORY(ScriptTrigger, "scripttrigger");
diff --git a/src/trigger/scripttrigger.hpp b/src/trigger/scripttrigger.hpp
new file mode 100644 (file)
index 0000000..7394734
--- /dev/null
@@ -0,0 +1,41 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SCRIPTTRIGGER_H__
+#define __SCRIPTTRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+
+class ScriptTrigger : public TriggerBase, public Serializable
+{
+public:
+  ScriptTrigger(const lisp::Lisp& reader);
+  ScriptTrigger(const Vector& pos, const std::string& script);
+  ~ScriptTrigger();
+
+  void write(lisp::Writer& writer);
+  void event(Player& player, EventType type);
+
+private:
+  EventType triggerevent;
+  std::string script;
+};
+
+#endif
diff --git a/src/trigger/secretarea_trigger.cpp b/src/trigger/secretarea_trigger.cpp
new file mode 100644 (file)
index 0000000..1c46495
--- /dev/null
@@ -0,0 +1,113 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "secretarea_trigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "gettext.hpp"
+#include "object/tilemap.hpp"
+
+static const float MESSAGE_TIME=3.5;
+
+SecretAreaTrigger::SecretAreaTrigger(const lisp::Lisp& reader)
+       : fade_tilemap("")
+{
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  float w = 32, h = 32;
+  reader.get("width", w);
+  reader.get("height", h);
+  bbox.set_size(w, h);
+  reader.get("fade-tilemap", fade_tilemap);
+
+  message_displayed = false;
+}
+
+SecretAreaTrigger::SecretAreaTrigger(const Rect& area, std::string fade_tilemap)
+       : fade_tilemap(fade_tilemap)
+{
+  bbox = area;
+  message_displayed = false;
+}
+
+SecretAreaTrigger::~SecretAreaTrigger()
+{
+}
+
+void
+SecretAreaTrigger::write(lisp::Writer& writer)
+{
+  writer.start_list("secretarea");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_float("width", bbox.get_width());
+  writer.write_float("height", bbox.get_height());
+  writer.write_string("fade-tilemap", fade_tilemap);
+
+  writer.end_list("secretarea");
+}
+
+void
+SecretAreaTrigger::draw(DrawingContext& context)
+{
+  if (message_timer.started()) {
+    context.push_transform();
+    context.set_translation(Vector(0, 0));
+    Vector pos = Vector(0, SCREEN_HEIGHT/2 - gold_text->get_height()/2);
+    context.draw_center_text(gold_text, _("You found a secret area!"), pos, LAYER_GUI);
+    context.pop_transform();
+  }
+  if (message_timer.check()) {
+    remove_me();
+  }
+}
+
+void
+SecretAreaTrigger::event(Player& , EventType type)
+{
+  if(type == EVENT_TOUCH) {
+    if (!message_displayed) {
+      message_timer.start(MESSAGE_TIME);
+      message_displayed = true;
+      Sector::current()->get_level()->stats.secrets++;
+
+      if (fade_tilemap != "") {
+       // fade away tilemaps
+       Sector& sector = *Sector::current();
+       for(Sector::GameObjects::iterator i = sector.gameobjects.begin(); i != sector.gameobjects.end(); ++i) {
+         TileMap* tm = dynamic_cast<TileMap*>(*i);
+         if (!tm) continue;
+         if (tm->get_name() != fade_tilemap) continue;
+         tm->fade(0.0, 1.0);
+       }
+      }
+
+    }
+  }
+}
+
+IMPLEMENT_FACTORY(SecretAreaTrigger, "secretarea");
diff --git a/src/trigger/secretarea_trigger.hpp b/src/trigger/secretarea_trigger.hpp
new file mode 100644 (file)
index 0000000..2f1130f
--- /dev/null
@@ -0,0 +1,46 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SECRETAREA_TRIGGER_H__
+#define __SECRETAREA_TRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "timer.hpp"
+
+class SecretAreaTrigger : public TriggerBase, public Serializable
+{
+public:
+  SecretAreaTrigger(const lisp::Lisp& reader);
+  SecretAreaTrigger(const Rect& area, std::string fade_tilemap = "");
+  ~SecretAreaTrigger();
+
+  void write(lisp::Writer& writer);
+  void event(Player& player, EventType type);
+  void draw(DrawingContext& context);
+
+private:
+  Timer message_timer;
+  bool message_displayed;
+  std::string fade_tilemap; /**< tilemap to fade away when trigger is activated, or empty if you don't care */
+};
+
+#endif
diff --git a/src/trigger/sequence_trigger.cpp b/src/trigger/sequence_trigger.cpp
new file mode 100644 (file)
index 0000000..5a94419
--- /dev/null
@@ -0,0 +1,75 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+#include <config.h>
+
+#include "sequence_trigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+
+SequenceTrigger::SequenceTrigger(const lisp::Lisp& reader)
+{
+  reader.get("x", bbox.p1.x);
+  reader.get("y", bbox.p1.y);
+  float w = 0, h = 0;
+  reader.get("width", w);
+  reader.get("height", h);
+  bbox.set_size(w, h);
+  reader.get("sequence", sequence_name);
+  triggerevent = EVENT_TOUCH;
+}
+
+SequenceTrigger::SequenceTrigger(const Vector& pos, const std::string& sequence)
+{
+  bbox.set_pos(pos);
+  bbox.set_size(32, 32);
+  sequence_name = sequence;
+  triggerevent = EVENT_TOUCH;
+}
+
+SequenceTrigger::~SequenceTrigger()
+{
+}
+
+void
+SequenceTrigger::write(lisp::Writer& writer)
+{
+  writer.start_list("sequencetrigger");
+
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_float("width", bbox.get_width());
+  writer.write_float("height", bbox.get_height());
+  writer.write_string("sequence", sequence_name);
+
+  writer.end_list("sequencetrigger");
+}
+
+void
+SequenceTrigger::event(Player& player, EventType type)
+{
+  if(type == triggerevent) {
+    player.trigger_sequence(sequence_name);
+  }
+}
+
+IMPLEMENT_FACTORY(SequenceTrigger, "sequencetrigger")
diff --git a/src/trigger/sequence_trigger.hpp b/src/trigger/sequence_trigger.hpp
new file mode 100644 (file)
index 0000000..199323a
--- /dev/null
@@ -0,0 +1,45 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+#ifndef __SEQUENCE_TRIGGER_H__
+#define __SEQUENCE_TRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "object/player.hpp"
+
+class SequenceTrigger : public TriggerBase, public Serializable
+{
+public:
+  SequenceTrigger(const lisp::Lisp& reader);
+  SequenceTrigger(const Vector& pos, const std::string& sequence);
+  ~SequenceTrigger();
+
+  void write(lisp::Writer& writer);
+  void event(Player& player, EventType type);
+
+  std::string get_sequence_name() const { return sequence_name; }
+
+private:
+  EventType triggerevent;
+  std::string sequence_name;
+};
+
+#endif
diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp
new file mode 100644 (file)
index 0000000..23f5754
--- /dev/null
@@ -0,0 +1,120 @@
+//  $Id$
+//
+//  SuperTux - Switch Trigger
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+#include <stdexcept>
+
+#include "switch.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "audio/sound_manager.hpp"
+
+namespace {
+ const std::string SWITCH_SOUND = "sounds/switch.ogg";
+}
+
+Switch::Switch(const lisp::Lisp& reader)
+       : state(OFF)
+{
+  if (!reader.get("x", bbox.p1.x)) throw std::runtime_error("no x position set");
+  if (!reader.get("y", bbox.p1.y)) throw std::runtime_error("no y position set");
+  if (!reader.get("sprite", sprite_name)) throw std::runtime_error("no sprite name set");
+  sprite = sprite_manager->create(sprite_name);
+  bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+  if (!reader.get("script", script)) throw std::runtime_error("no script set");
+  sound_manager->preload( SWITCH_SOUND );
+}
+
+Switch::~Switch()
+{
+  delete sprite;
+}
+
+void
+Switch::write(lisp::Writer& writer)
+{
+  writer.start_list("switch");
+  writer.write_float("x", bbox.p1.x);
+  writer.write_float("y", bbox.p1.y);
+  writer.write_string("sprite", sprite_name);
+  writer.write_string("script", script);
+  writer.end_list("switch");
+}
+
+void
+Switch::update(float )
+{
+  switch (state) {
+    case OFF:
+      break;
+    case TURN_ON:
+      if(sprite->animation_done()) {
+       std::istringstream stream(script);
+       Sector::current()->run_script(stream, "Switch");
+
+       sprite->set_action("on", 1);
+       state = ON;
+      }
+      break;
+    case ON:
+      if(sprite->animation_done()) {
+       sprite->set_action("turnoff", 1);
+       state = TURN_OFF;
+      }
+      break;
+    case TURN_OFF:
+      if(sprite->animation_done()) {
+       sprite->set_action("off");
+       state = OFF;
+      }
+      break;
+  }
+}
+
+void
+Switch::draw(DrawingContext& context)
+{
+  sprite->draw(context, bbox.p1, LAYER_TILES);
+}
+
+void
+Switch::event(Player& , EventType type)
+{
+  if(type != EVENT_ACTIVATE) return;
+
+  switch (state) {
+    case OFF:
+       sprite->set_action("turnon", 1);
+        sound_manager->play( SWITCH_SOUND );
+       state = TURN_ON;
+      break;
+    case TURN_ON:
+      break;
+    case ON:
+      break;
+    case TURN_OFF:
+      break;
+  }
+
+}
+
+IMPLEMENT_FACTORY(Switch, "switch");
diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp
new file mode 100644 (file)
index 0000000..8742f80
--- /dev/null
@@ -0,0 +1,60 @@
+//  $Id$
+//
+//  SuperTux - Switch Trigger
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SWITCH_H
+#define SUPERTUX_SWITCH_H
+
+#include <string>
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite.hpp"
+
+class Switch : public TriggerBase, public Serializable
+{
+public:
+  Switch(const lisp::Lisp& reader);
+  virtual ~Switch();
+
+  virtual void write(lisp::Writer& writer);
+
+  virtual void update(float elapsed_time);
+  virtual void draw(DrawingContext& context);
+  virtual void event(Player& player, EventType type);
+
+private:
+  enum SwitchState {
+    OFF,
+    TURN_ON,
+    ON,
+    TURN_OFF
+  };
+
+  std::string sprite_name;
+  Sprite* sprite;
+  std::string script;
+
+  SwitchState state;
+
+};
+
+#endif
diff --git a/src/trigger/trigger_base.cpp b/src/trigger/trigger_base.cpp
new file mode 100644 (file)
index 0000000..a221b29
--- /dev/null
@@ -0,0 +1,94 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "trigger_base.hpp"
+#include "video/drawing_context.hpp"
+#include "object/player.hpp"
+#include "log.hpp"
+
+TriggerBase::TriggerBase()
+  : sprite(0), lasthit(false), hit(false)
+{
+  set_group(COLGROUP_TOUCHABLE);
+}
+
+TriggerBase::~TriggerBase()
+{
+  // unregister remove_listener hooks, so nobody will try to call us after we've been destroyed
+  for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+    Player* p = *i;
+    p->del_remove_listener(this);
+  }
+  losetouch_listeners.clear();
+}
+
+void
+TriggerBase::update(float )
+{
+  if (lasthit && !hit) {
+    for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+      Player* p = *i;
+      event(*p, EVENT_LOSETOUCH);
+      p->del_remove_listener(this);
+    }
+    losetouch_listeners.clear();
+  }
+  lasthit = hit;
+  hit = false;
+}
+
+void
+TriggerBase::draw(DrawingContext& context)
+{
+  if(!sprite)
+    return;
+
+  sprite->draw(context, get_pos(), LAYER_TILES+1);
+}
+
+HitResponse
+TriggerBase::collision(GameObject& other, const CollisionHit& )
+{
+  Player* player = dynamic_cast<Player*> (&other);
+  if(player) {
+    hit = true;
+    if(!lasthit) {
+      losetouch_listeners.push_back(player);
+      player->add_remove_listener(this);
+      event(*player, EVENT_TOUCH);
+    }
+  }
+
+  return ABORT_MOVE;
+}
+  
+void 
+TriggerBase::object_removed(GameObject* object)
+{
+  for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+    Player* p = *i;
+    if (p == object) {
+      losetouch_listeners.erase(i);
+      break;
+    }
+  }
+}
+
diff --git a/src/trigger/trigger_base.hpp b/src/trigger/trigger_base.hpp
new file mode 100644 (file)
index 0000000..571dac5
--- /dev/null
@@ -0,0 +1,68 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_TRIGGER_BASE_H
+#define SUPERTUX_TRIGGER_BASE_H
+
+#include <list>
+#include "moving_object.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite.hpp"
+#include "object_remove_listener.hpp"
+
+class Player;
+
+/** This class is the base class for all objects you can interact with in some
+ * way. There are several interaction types defined like touch and activate
+ */
+class TriggerBase : public MovingObject, public ObjectRemoveListener
+{
+public:
+  enum EventType {
+    EVENT_TOUCH,     /**< Object came into contact */
+    EVENT_LOSETOUCH, /**< Lost contact with object */
+    EVENT_ACTIVATE   /**< Action button pressed    */
+  };
+
+  TriggerBase();
+  ~TriggerBase();
+
+  void update(float elapsed_time);
+  void draw(DrawingContext& context);
+  HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+  /**
+   * Receive trigger events
+   */
+  virtual void event(Player& player, EventType type) = 0;
+  
+  /**
+   * Called by GameObject destructor of an object in losetouch_listeners
+   */
+  virtual void object_removed(GameObject* object);
+
+private:
+  Sprite* sprite;
+  bool lasthit;
+  bool hit;
+
+  std::list<Player*> losetouch_listeners; /**< Players that will be informed when we lose touch with them */
+};
+
+#endif /*SUPERTUX_INTERACTIVE_OBJECT_H*/
diff --git a/src/video/color.cpp b/src/video/color.cpp
new file mode 100644 (file)
index 0000000..46bccc4
--- /dev/null
@@ -0,0 +1,30 @@
+//  $Id: color.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "color.hpp"
+
+const Color Color::BLACK(0.0, 0.0, 0.0);
+const Color Color::RED(1.0, 0.0, 0.0);
+const Color Color::GREEN(0.0, 1.0, 0.0);
+const Color Color::BLUE(0.0, 0.0, 1.0);
+const Color Color::CYAN(0.0, 1.0, 1.0);
+const Color Color::MAGENTA(1.0, 0.0, 1.0);
+const Color Color::YELLOW(1.0, 1.0, 0.0);
+const Color Color::WHITE(1.0, 1.0, 1.0);
diff --git a/src/video/color.hpp b/src/video/color.hpp
new file mode 100644 (file)
index 0000000..f0bd76d
--- /dev/null
@@ -0,0 +1,91 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __COLOR_HPP__
+#define __COLOR_HPP__
+
+#include <vector>
+#include <assert.h>
+#include "log.hpp"
+
+class Color
+{
+public:
+  Color()
+    : red(0), green(0), blue(0), alpha(1.0)
+  { }
+  Color(float red, float green, float blue, float alpha = 1.0)
+    : red(red), green(green), blue(blue), alpha(alpha)
+  {
+#ifdef DEBUG
+    check_color_ranges();
+#endif
+  }
+  Color(const std::vector<float>& vals)
+  {
+    assert(vals.size() >= 3);
+    red = vals[0];
+    green = vals[1];
+    blue = vals[2];
+    if(vals.size() > 3)
+      alpha = vals[3];
+    else
+      alpha = 1.0;
+#ifdef DEBUG
+    check_color_ranges();
+#endif
+  }
+
+  bool operator==(const Color& other) const
+  {
+    return red == other.red && green == other.green && blue == other.blue
+           && alpha == other.alpha;
+  }
+
+  void check_color_ranges()
+  {
+    if(red < 0 || red > 1.0 || green < 0 || green > 1.0
+            || blue < 0 || blue > 1.0
+            || alpha < 0 || alpha > 1.0)
+      log_warning << "color value out of range: " << red << ", " << green << ", " << blue << ", " << alpha << std::endl;
+  }
+
+  float greyscale() const
+  {
+    return red * 0.30 + green * 0.59 + blue * 0.11;
+  }
+
+  bool operator < (const Color& other) const
+  {
+    return greyscale() < other.greyscale();
+  }
+
+  float red, green, blue, alpha;
+
+  static const Color BLACK;
+  static const Color RED;
+  static const Color GREEN;
+  static const Color BLUE;
+  static const Color CYAN;
+  static const Color MAGENTA;
+  static const Color YELLOW;
+  static const Color WHITE;
+};
+
+#endif
diff --git a/src/video/drawing_context.cpp b/src/video/drawing_context.cpp
new file mode 100644 (file)
index 0000000..7ca3e26
--- /dev/null
@@ -0,0 +1,469 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "video_systems.hpp"
+#include "renderer.hpp"
+#include "lightmap.hpp"
+#include "surface.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+static inline int next_po2(int val)
+{
+  int result = 1;
+  while(result < val)
+    result *= 2;
+
+  return result;
+}
+
+DrawingContext::DrawingContext() :
+  renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
+{
+  requests = &drawing_requests;
+  obstack_init(&obst);
+}
+
+DrawingContext::~DrawingContext()
+{
+  delete renderer;
+  delete lightmap;
+
+  obstack_free(&obst, NULL);
+}
+
+void
+DrawingContext::init_renderer()
+{
+  delete renderer;
+  delete lightmap;
+
+  renderer = new_renderer();
+  lightmap = new_lightmap();
+}
+
+void
+DrawingContext::draw_surface(const Surface* surface, const Vector& position,
+                             float angle, const Color& color, const Blend& blend,
+                             int layer)
+{
+  assert(surface != 0);
+
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = SURFACE;
+  request->pos = transform.apply(position);
+
+  if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
+      || request->pos.x + surface->get_width() < 0
+      || request->pos.y + surface->get_height() < 0)
+    return;
+
+  request->layer = layer;
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+  request->angle = angle;
+  request->color = color;
+  request->blend = blend;
+
+  request->request_data = const_cast<Surface*> (surface);
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::draw_surface(const Surface* surface, const Vector& position,
+    int layer)
+{
+  draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
+}
+
+void
+DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
+    const Vector& size, const Vector& dest, int layer)
+{
+  assert(surface != 0);
+
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = SURFACE_PART;
+  request->pos = transform.apply(dest);
+  request->layer = layer;
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+
+  SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
+  surfacepartrequest->size = size;
+  surfacepartrequest->source = source;
+  surfacepartrequest->surface = surface;
+
+  // clip on screen borders
+  if(request->pos.x < 0) {
+    surfacepartrequest->size.x += request->pos.x;
+    if(surfacepartrequest->size.x <= 0)
+      return;
+    surfacepartrequest->source.x -= request->pos.x;
+    request->pos.x = 0;
+  }
+  if(request->pos.y < 0) {
+    surfacepartrequest->size.y += request->pos.y;
+    if(surfacepartrequest->size.y <= 0)
+      return;
+    surfacepartrequest->source.y -= request->pos.y;
+    request->pos.y = 0;
+  }
+  request->request_data = surfacepartrequest;
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::draw_text(const Font* font, const std::string& text,
+    const Vector& position, FontAlignment alignment, int layer)
+{
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = TEXT;
+  request->pos = transform.apply(position);
+  request->layer = layer;
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+
+  TextRequest* textrequest = new(obst) TextRequest();
+  textrequest->font = font;
+  textrequest->text = text;
+  textrequest->alignment = alignment;
+  request->request_data = textrequest;
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::draw_center_text(const Font* font, const std::string& text,
+    const Vector& position, int layer)
+{
+  draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
+      ALIGN_CENTER, layer);
+}
+
+void
+DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
+{
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = GRADIENT;
+  request->pos = Vector(0,0);
+  request->layer = layer;
+
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+
+  GradientRequest* gradientrequest = new(obst) GradientRequest();
+  gradientrequest->top = top;
+  gradientrequest->bottom = bottom;
+  request->request_data = gradientrequest;
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
+                                 const Color& color, int layer)
+{
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = FILLRECT;
+  request->pos = transform.apply(topleft);
+  request->layer = layer;
+
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+
+  FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
+  fillrectrequest->size = size;
+  fillrectrequest->color = color;
+  fillrectrequest->color.alpha = color.alpha * transform.alpha;
+  request->request_data = fillrectrequest;
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
+                                 int layer)
+{
+  DrawingRequest* request = new(obst) DrawingRequest();
+
+  request->target = target;
+  request->type = FILLRECT;
+  request->pos = transform.apply(rect.p1);
+  request->layer = layer;
+
+  request->drawing_effect = transform.drawing_effect;
+  request->alpha = transform.alpha;
+
+  FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
+  fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
+  fillrectrequest->color = color;
+  fillrectrequest->color.alpha = color.alpha * transform.alpha;
+  request->request_data = fillrectrequest;
+
+  requests->push_back(request);
+}
+
+void
+DrawingContext::get_light(const Vector& position, Color* color)
+{
+  if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
+      && ambient_color.blue  == 1.0f ) {
+    *color = Color( 1.0f, 1.0f, 1.0f);
+    return;
+  }
+
+  DrawingRequest* request = new(obst) DrawingRequest();
+  request->target = target;
+  request->type = GETLIGHT;
+  request->pos = transform.apply(position);
+
+  //There is no light offscreen.
+  if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
+      || request->pos.x < 0 || request->pos.y < 0){
+    *color = Color( 0, 0, 0);
+    return;
+  }
+
+  request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
+  GetLightRequest* getlightrequest = new(obst) GetLightRequest();
+  getlightrequest->color_ptr = color;
+  request->request_data = getlightrequest;
+  lightmap_requests.push_back(request);
+}
+
+void
+DrawingContext::do_drawing()
+{
+#ifdef DEBUG
+  assert(transformstack.empty());
+  assert(target_stack.empty());
+#endif
+  transformstack.clear();
+  target_stack.clear();
+
+  //Use Lightmap if ambient color is not white.
+  bool use_lightmap = ( ambient_color.red != 1.0f   || ambient_color.green != 1.0f ||
+                        ambient_color.blue  != 1.0f );
+
+  // PART1: create lightmap
+  if(use_lightmap) {
+    lightmap->start_draw(ambient_color);
+    handle_drawing_requests(lightmap_requests);
+    lightmap->end_draw();
+  }
+
+  handle_drawing_requests(drawing_requests);
+  if(use_lightmap) {
+    lightmap->do_draw();
+  }
+  obstack_free(&obst, NULL);
+  obstack_init(&obst);
+
+  // if a screenshot was requested, take one
+  if (screenshot_requested) {
+    renderer->do_take_screenshot();
+    screenshot_requested = false;
+  }
+
+  renderer->flip();
+}
+
+class RequestPtrCompare
+  :  public std::binary_function<const DrawingRequest*,
+                                 const DrawingRequest*, 
+                                 bool>
+{
+public:
+  bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
+  {
+    return *r1 < *r2;
+  }
+};
+
+void
+DrawingContext::handle_drawing_requests(DrawingRequests& requests)
+{
+  std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
+
+  DrawingRequests::const_iterator i;
+  for(i = requests.begin(); i != requests.end(); ++i) {
+    const DrawingRequest& request = **i;
+
+    switch(request.target) {
+      case NORMAL:
+        switch(request.type) {
+          case SURFACE:
+            renderer->draw_surface(request);
+            break;
+          case SURFACE_PART:
+            renderer->draw_surface_part(request);
+            break;
+          case GRADIENT:
+            renderer->draw_gradient(request);
+            break;
+          case TEXT:
+            {
+              const TextRequest* textrequest = (TextRequest*) request.request_data;
+              textrequest->font->draw(renderer, textrequest->text, request.pos,
+                  textrequest->alignment, request.drawing_effect, request.alpha);
+            }
+            break;
+          case FILLRECT:
+            renderer->draw_filled_rect(request);
+            break;
+          case GETLIGHT:
+            lightmap->get_light(request);
+            break;
+        }
+        break;
+      case LIGHTMAP:
+        switch(request.type) {
+          case SURFACE:
+            lightmap->draw_surface(request);
+            break;
+          case SURFACE_PART:
+            lightmap->draw_surface_part(request);
+            break;
+          case GRADIENT:
+            lightmap->draw_gradient(request);
+            break;
+          case TEXT:
+            {
+              const TextRequest* textrequest = (TextRequest*) request.request_data;
+              textrequest->font->draw(renderer, textrequest->text, request.pos,
+                  textrequest->alignment, request.drawing_effect, request.alpha);
+            }
+            break;
+          case FILLRECT:
+            lightmap->draw_filled_rect(request);
+            break;
+          case GETLIGHT:
+            lightmap->get_light(request);
+            break;
+        }
+        break;
+    }
+  }
+  requests.clear();
+}
+
+void
+DrawingContext::push_transform()
+{
+  transformstack.push_back(transform);
+}
+
+void
+DrawingContext::pop_transform()
+{
+  assert(!transformstack.empty());
+
+  transform = transformstack.back();
+  transformstack.pop_back();
+}
+
+void
+DrawingContext::set_drawing_effect(DrawingEffect effect)
+{
+  transform.drawing_effect = effect;
+}
+
+DrawingEffect
+DrawingContext::get_drawing_effect() const
+{
+  return transform.drawing_effect;
+}
+
+void
+DrawingContext::set_alpha(float alpha)
+{
+  transform.alpha = alpha;
+}
+
+float
+DrawingContext::get_alpha() const
+{
+  return transform.alpha;
+}
+
+void
+DrawingContext::push_target()
+{
+  target_stack.push_back(target);
+}
+
+void
+DrawingContext::pop_target()
+{
+  set_target(target_stack.back());
+  target_stack.pop_back();
+}
+
+void
+DrawingContext::set_target(Target target)
+{
+  this->target = target;
+  if(target == LIGHTMAP) {
+    requests = &lightmap_requests;
+  } else {
+    assert(target == NORMAL);
+    requests = &drawing_requests;
+  }
+}
+
+void
+DrawingContext::set_ambient_color( Color new_color )
+{
+  ambient_color = new_color;
+}
+
+void 
+DrawingContext::take_screenshot()
+{
+  screenshot_requested = true;
+}
+
diff --git a/src/video/drawing_context.hpp b/src/video/drawing_context.hpp
new file mode 100644 (file)
index 0000000..c8f9012
--- /dev/null
@@ -0,0 +1,169 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_DRAWINGCONTEXT_H
+#define SUPERTUX_DRAWINGCONTEXT_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "drawing_request.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Surface;
+class Texture;
+struct DrawingRequest;
+class Renderer;
+class Lightmap;
+
+/**
+ * This class provides functions for drawing things on screen. It also
+ * maintains a stack of transforms that are applied to graphics.
+ */
+class DrawingContext
+{
+public:
+  DrawingContext();
+  ~DrawingContext();
+
+  void init_renderer();
+
+  /// Adds a drawing request for a surface into the request list.
+  void draw_surface(const Surface* surface, const Vector& position,
+                    int layer);
+  /// Adds a drawing request for a surface into the request list.
+  void draw_surface(const Surface* surface, const Vector& position,
+                    float angle, const Color& color, const Blend& blend,
+                    int layer);
+  /// Adds a drawing request for part of a surface.
+  void draw_surface_part(const Surface* surface, const Vector& source,
+                         const Vector& size, const Vector& dest, int layer);
+  /// Draws a text.
+  void draw_text(const Font* font, const std::string& text,
+                 const Vector& position, FontAlignment alignment, int layer);
+
+  /// Draws text on screen center (feed Vector.x with a 0).
+  /// This is the same as draw_text() with a SCREEN_WIDTH/2 position and
+  /// alignment set to LEFT_ALLIGN
+  void draw_center_text(const Font* font, const std::string& text,
+                        const Vector& position, int layer);
+  /// Draws a color gradient onto the whole screen */
+  void draw_gradient(const Color& from, const Color& to, int layer);
+  /// Fills a rectangle.
+  void draw_filled_rect(const Vector& topleft, const Vector& size,
+                        const Color& color, int layer);
+  void draw_filled_rect(const Rect& rect, const Color& color, int layer);
+
+  /// Processes all pending drawing requests and flushes the list.
+  void do_drawing();
+
+  const Vector& get_translation() const
+  {  return transform.translation;  }
+
+  void set_translation(const Vector& newtranslation)
+  {  transform.translation = newtranslation;  }
+
+  void push_transform();
+  void pop_transform();
+
+  /// Apply that effect in the next draws (effects are listed on surface.h).
+  void set_drawing_effect(DrawingEffect effect);
+  /// return currently applied drawing effect
+  DrawingEffect get_drawing_effect() const;
+  /// apply that alpha in the next draws (1.0 means fully opaque) */
+  void set_alpha(float alpha);
+  /// return currently set alpha
+  float get_alpha() const;
+
+  /// on next update, set color to lightmap's color at position
+  void get_light(const Vector& position, Color* color );
+
+  typedef ::Target Target;
+  static const Target NORMAL = ::NORMAL;
+  static const Target LIGHTMAP = ::LIGHTMAP;
+  void push_target();
+  void pop_target();
+  void set_target(Target target);
+
+  void set_ambient_color( Color new_color );
+
+  /**
+   * requests that a screenshot be taken after the next frame has been rendered
+   */
+  void take_screenshot();
+
+private:
+  class Transform
+  {
+  public:
+    Vector translation;
+    DrawingEffect drawing_effect;
+    float alpha;
+
+    Transform()
+      : drawing_effect(NO_EFFECT), alpha(1.0f)
+    { }
+
+    Vector apply(const Vector& v) const
+    {
+      return v - translation;
+    }
+  };
+
+  Renderer *renderer;
+  Lightmap *lightmap;
+
+  /// the transform stack
+  std::vector<Transform> transformstack;
+  /// the currently active transform
+  Transform transform;
+
+  std::vector<Blend> blend_stack;
+  Blend blend_mode;
+
+  typedef std::vector<DrawingRequest*> DrawingRequests;
+
+  void handle_drawing_requests(DrawingRequests& requests);
+
+  DrawingRequests drawing_requests;
+  DrawingRequests lightmap_requests;
+
+  DrawingRequests* requests;
+  Color ambient_color;
+
+  Target target;
+  std::vector<Target> target_stack;
+
+  /* obstack holding the memory of the drawing requests */
+  struct obstack obst;
+
+  bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
+};
+
+#endif
+
diff --git a/src/video/drawing_request.hpp b/src/video/drawing_request.hpp
new file mode 100644 (file)
index 0000000..24e0c02
--- /dev/null
@@ -0,0 +1,133 @@
+//  $Id: drawing_request.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_DRAWINGREQUEST_H
+#define SUPERTUX_DRAWINGREQUEST_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "math/vector.hpp"
+#include "color.hpp"
+#include "font.hpp"
+
+class Surface;
+
+// some constants for predefined layer values
+enum {
+  LAYER_BACKGROUND0 = -300,
+  LAYER_BACKGROUND1 = -200,
+  LAYER_BACKGROUNDTILES = -100,
+  LAYER_TILES = 0,
+  LAYER_OBJECTS = 50,
+  LAYER_FLOATINGOBJECTS = 150,
+  LAYER_FOREGROUNDTILES = 200,
+  LAYER_FOREGROUND0 = 300,
+  LAYER_FOREGROUND1 = 400,
+  LAYER_HUD = 500,
+  LAYER_GUI         = 600
+};
+
+class Blend
+{
+public:
+  GLenum sfactor;
+  GLenum dfactor;
+
+  Blend()
+    : sfactor(GL_SRC_ALPHA), dfactor(GL_ONE_MINUS_SRC_ALPHA)
+  {}
+
+  Blend(GLenum s, GLenum d)
+    : sfactor(s), dfactor(d)
+  {}
+};
+
+enum Target {
+  NORMAL, LIGHTMAP
+};
+
+enum RequestType
+{
+  SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, GETLIGHT
+};
+
+struct SurfacePartRequest
+{
+  const Surface* surface;
+  Vector source, size;
+};
+
+struct TextRequest
+{
+  const Font* font;
+  std::string text;
+  FontAlignment alignment;
+};
+
+struct GradientRequest
+{
+  Color top, bottom;
+  Vector size;
+};
+
+struct FillRectRequest
+{
+  Color color;
+  Vector size;
+};
+
+struct DrawingRequest
+{
+  Target target;
+  RequestType type;
+  Vector pos;
+
+  int layer;
+  DrawingEffect drawing_effect;
+  float alpha;
+  Blend blend;
+  float angle;
+  Color color;
+
+  void* request_data;
+
+  DrawingRequest()
+    : angle(0.0f),
+      color(1.0f, 1.0f, 1.0f, 1.0f)
+  {}
+
+  bool operator<(const DrawingRequest& other) const
+  {
+    return layer < other.layer;
+  }
+};
+
+struct GetLightRequest
+{
+  Color* color_ptr;
+};
+
+#endif
+
diff --git a/src/video/font.cpp b/src/video/font.cpp
new file mode 100644 (file)
index 0000000..e5b6bb1
--- /dev/null
@@ -0,0 +1,448 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//                     Ingo Ruhnke <grumbel@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+#include <SDL_image.h>
+#include "physfs/physfs_sdl.hpp"
+
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "screen.hpp"
+#include "font.hpp"
+#include "renderer.hpp"
+#include "drawing_context.hpp"
+#include "log.hpp"
+
+namespace {
+bool     has_multibyte_mark(unsigned char c);
+uint32_t decode_utf8(const std::string& text, size_t& p);
+
+struct UTF8Iterator
+{
+  const std::string&     text;
+  std::string::size_type pos;
+  uint32_t chr;
+
+  UTF8Iterator(const std::string& text_)
+    : text(text_),
+      pos(0)
+  {
+    chr = decode_utf8(text, pos);
+  }
+
+  bool done() const
+  {
+    return pos > text.size();
+  }
+
+  UTF8Iterator& operator++() {
+    try {
+      chr = decode_utf8(text, pos);
+    } catch (std::runtime_error) {
+      log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + pos)) << " found " << std::endl;
+      chr = 0;
+      ++pos;
+    }
+
+    return *this;
+  }
+
+  uint32_t operator*() const {
+    return chr;
+  }
+};
+
+bool vline_empty(SDL_Surface* surface, int x, int start_y, int end_y, Uint8 threshold)
+{
+  Uint8* pixels = (Uint8*)surface->pixels;
+
+  for(int y = start_y; y < end_y; ++y)
+    {
+      const Uint8& p = pixels[surface->pitch*y + x*surface->format->BytesPerPixel + 3];
+      if (p > threshold)
+        {
+          return false;
+        }
+    }
+  return true;
+}
+} // namespace
+
+Font::Font(GlyphWidth glyph_width_,
+           const std::string& filename,
+           const std::string& shadowfile,
+           int char_width, int char_height_,
+           int shadowsize_)
+  : glyph_width(glyph_width_),
+    glyph_surface(0), shadow_glyph_surface(0),
+    char_height(char_height_),
+    shadowsize(shadowsize_)
+{
+  glyph_surface = new Surface(filename);
+  shadow_glyph_surface  = new Surface(shadowfile);
+
+  first_char = 32;
+  char_count = ((int) glyph_surface->get_height() / char_height) * 16;
+
+  if (glyph_width == FIXED)
+    {
+      for(uint32_t i = 0; i < char_count; ++i)
+        {
+          float x = (i % 16) * char_width;
+          float y = (i / 16) * char_height;
+
+          Glyph glyph;
+          glyph.advance = char_width;
+          glyph.offset  = Vector(0, 0);
+          glyph.rect    = Rect(x, y, x + char_width, y + char_height);
+
+          glyphs.push_back(glyph);
+          shadow_glyphs.push_back(glyph);
+        }
+    }
+  else // glyph_width == VARIABLE
+    {
+      // Load the surface into RAM and scan the pixel data for characters
+      SDL_Surface* surface = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
+      if(surface == NULL) {
+        std::ostringstream msg;
+        msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
+        throw std::runtime_error(msg.str());
+      }
+
+      SDL_LockSurface(surface);
+
+      for(uint32_t i = 0; i < char_count; ++i)
+        {
+          int x = (i % 16) * char_width;
+          int y = (i / 16) * char_height;
+
+          int left = x;
+          while (left < x + char_width &&
+                 vline_empty(surface, left, y, y + char_height, 64))
+            left += 1;
+
+          int right = x + char_width - 1;
+          while (right > left &&
+                 vline_empty(surface, right, y, y + char_height, 64))
+            right -= 1;
+
+          Glyph glyph;
+          glyph.offset = Vector(0, 0);
+
+          if (left <= right)
+            glyph.rect = Rect(left,  y, right+1, y + char_height);
+          else // glyph is completly transparent
+            glyph.rect = Rect(x,  y, x + char_width, y + char_height);
+
+          glyph.advance = glyph.rect.get_width() + 1; // FIXME: might be usefull to make spacing configurable
+
+          glyphs.push_back(glyph);
+          shadow_glyphs.push_back(glyph);
+        }
+
+      SDL_UnlockSurface(surface);
+
+      SDL_FreeSurface(surface);
+    }
+}
+
+Font::~Font()
+{
+  delete glyph_surface;
+  delete shadow_glyph_surface;
+}
+
+float
+Font::get_text_width(const std::string& text) const
+{
+  float curr_width = 0;
+  float last_width = 0;
+
+  for(UTF8Iterator it(text); !it.done(); ++it)
+    {
+      if (*it == '\n')
+        {
+          last_width = std::max(last_width, curr_width);
+          curr_width = 0;
+        }
+      else
+        {
+          int idx = chr2glyph(*it);
+          curr_width += glyphs[idx].advance;
+        }
+    }
+
+  return std::max(curr_width, last_width);
+}
+
+float
+Font::get_text_height(const std::string& text) const
+{
+  std::string::size_type text_height = char_height;
+
+  for(std::string::const_iterator it = text.begin(); it != text.end(); ++it)
+    { // since UTF8 multibyte characters are decoded with values
+      // outside the ASCII range there is no risk of overlapping and
+      // thus we don't need to decode the utf-8 string
+      if (*it == '\n')
+        text_height += char_height + 2;
+    }
+
+  return text_height;
+}
+
+float
+Font::get_height() const
+{
+  return char_height;
+}
+
+std::string
+Font::wrap_to_chars(const std::string& s, int line_length, std::string* overflow)
+{
+  // if text is already smaller, return full text
+  if ((int)s.length() <= line_length) {
+    if (overflow) *overflow = "";
+    return s;
+  }
+
+  // if we can find a whitespace character to break at, return text up to this character
+  int i = line_length;
+  while ((i > 0) && (s[i] != ' ')) i--;
+  if (i > 0) {
+    if (overflow) *overflow = s.substr(i+1);
+    return s.substr(0, i);
+  }
+
+  // FIXME: wrap at line_length, taking care of multibyte characters
+  if (overflow) *overflow = "";
+  return s;
+}
+
+std::string
+Font::wrap_to_width(const std::string& s, float width, std::string* overflow)
+{
+  // if text is already smaller, return full text
+  if (get_text_width(s) <= width) {
+    if (overflow) *overflow = "";
+    return s;
+  }
+
+  // if we can find a whitespace character to break at, return text up to this character
+  for (int i = s.length()-1; i >= 0; i--) {
+    std::string s2 = s.substr(0,i);
+    if (s[i] != ' ') continue;
+    if (get_text_width(s2) <= width) {
+      if (overflow) *overflow = s.substr(i+1);
+      return s.substr(0, i);
+    }
+  }
+  
+  // FIXME: hard-wrap at width, taking care of multibyte characters
+  if (overflow) *overflow = "";
+  return s;
+}
+
+void
+Font::draw(Renderer *renderer, const std::string& text, const Vector& pos_,
+           FontAlignment alignment, DrawingEffect drawing_effect,
+           float alpha) const
+{
+  float x = pos_.x;
+  float y = pos_.y;
+
+  std::string::size_type last = 0;
+  for(std::string::size_type i = 0;; ++i)
+    {
+      if (text[i] == '\n' || i == text.size())
+        {
+          std::string temp = text.substr(last, i - last);
+
+          // calculate X positions based on the alignment type
+          Vector pos = Vector(x, y);
+
+          if(alignment == ALIGN_CENTER)
+            pos.x -= get_text_width(temp) / 2;
+          else if(alignment == ALIGN_RIGHT)
+            pos.x -= get_text_width(temp);
+
+          // Cast font position to integer to get a clean drawing result and
+          // no bluring as we would get with subpixel positions
+          pos.x = static_cast<int>(pos.x);
+
+          draw_text(renderer, temp, pos, drawing_effect, alpha);
+
+          if (i == text.size())
+            break;
+
+          y += char_height + 2;
+          last = i + 1;
+        }
+    }
+}
+
+void
+Font::draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+                DrawingEffect drawing_effect, float alpha) const
+{
+  if(shadowsize > 0)
+    {
+      // FIXME: shadow_glyph_surface and glyph_surface do currently
+      // share the same glyph array, this is incorrect and should be
+      // fixed, it is however hardly noticable
+      draw_chars(renderer, shadow_glyph_surface, text,
+                 pos + Vector(shadowsize, shadowsize), drawing_effect, alpha);
+    }
+
+  draw_chars(renderer, glyph_surface, text, pos, drawing_effect, alpha);
+}
+
+int
+Font::chr2glyph(uint32_t chr) const
+{
+  int glyph_index = chr - first_char;
+
+  // we don't have the control chars 0x80-0xa0 in the font
+  if (chr >= 0x80) { // non-ascii character
+    glyph_index -= 32;
+    if(chr <= 0xa0) {
+      log_debug << "Unsupported utf-8 character '" << chr << "' found" << std::endl;
+      glyph_index = 0;
+    }
+  }
+
+  if(glyph_index < 0 || glyph_index >= (int) char_count) {
+    log_debug << "Unsupported utf-8 character found" << std::endl;
+    glyph_index = 0;
+  }
+
+  return glyph_index;
+}
+
+void
+Font::draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+                 const Vector& pos, DrawingEffect drawing_effect,
+                 float alpha) const
+{
+  Vector p = pos;
+
+  for(UTF8Iterator it(text); !it.done(); ++it)
+    {
+      int font_index = chr2glyph(*it);
+
+      if(*it == '\n')
+        {
+          p.x = pos.x;
+          p.y += char_height + 2;
+        }
+      else if(*it == ' ')
+        {
+          p.x += glyphs[font_index].advance;
+        }
+      else
+        {
+          const Glyph& glyph = glyphs[font_index];
+          DrawingRequest request;
+
+          request.pos = p + glyph.offset;
+          request.drawing_effect = drawing_effect;
+          request.alpha = alpha;
+
+          SurfacePartRequest surfacepartrequest;
+          surfacepartrequest.size = glyph.rect.p2 - glyph.rect.p1;
+          surfacepartrequest.source = glyph.rect.p1;
+          surfacepartrequest.surface = pchars;
+
+          request.request_data = &surfacepartrequest;
+          renderer->draw_surface_part(request);
+
+          p.x += glyphs[font_index].advance;
+        }
+    }
+}
+
+
+namespace {
+
+/**
+ * returns true if this byte matches a bitmask of 10xx.xxxx, i.e. it is the 2nd, 3rd or 4th byte of a multibyte utf8 string
+ */
+bool has_multibyte_mark(unsigned char c) {
+  return ((c & 0300) == 0200);
+}
+
+/**
+ * gets unicode character at byte position @a p of UTF-8 encoded @a
+ * text, then advances @a p to the next character.
+ *
+ * @throws std::runtime_error if decoding fails.
+ * See unicode standard section 3.10 table 3-5 and 3-6 for details.
+ */
+uint32_t decode_utf8(const std::string& text, size_t& p)
+{
+  uint32_t c1 = (unsigned char) text[p+0];
+
+  if (has_multibyte_mark(c1)) std::runtime_error("Malformed utf-8 sequence");
+
+  if ((c1 & 0200) == 0000) {
+    // 0xxx.xxxx: 1 byte sequence
+    p+=1;
+    return c1;
+  }
+  else if ((c1 & 0340) == 0300) {
+    // 110x.xxxx: 2 byte sequence
+    if(p+1 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+    uint32_t c2 = (unsigned char) text[p+1];
+    if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+    p+=2;
+    return (c1 & 0037) << 6 | (c2 & 0077);
+  }
+  else if ((c1 & 0360) == 0340) {
+    // 1110.xxxx: 3 byte sequence
+    if(p+2 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+    uint32_t c2 = (unsigned char) text[p+1];
+    uint32_t c3 = (unsigned char) text[p+2];
+    if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+    if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
+    p+=3;
+    return (c1 & 0017) << 12 | (c2 & 0077) << 6 | (c3 & 0077);
+  }
+  else if ((c1 & 0370) == 0360) {
+    // 1111.0xxx: 4 byte sequence
+    if(p+3 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+    uint32_t c2 = (unsigned char) text[p+1];
+    uint32_t c3 = (unsigned char) text[p+2];
+    uint32_t c4 = (unsigned char) text[p+4];
+    if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+    if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
+    if (!has_multibyte_mark(c4)) throw std::runtime_error("Malformed utf-8 sequence");
+    p+=4;
+    return (c1 & 0007) << 18 | (c2 & 0077) << 12 | (c3 & 0077) << 6 | (c4 & 0077);
+  }
+  throw std::runtime_error("Malformed utf-8 sequence");
+}
+
+} // namespace
diff --git a/src/video/font.hpp b/src/video/font.hpp
new file mode 100644 (file)
index 0000000..5625be1
--- /dev/null
@@ -0,0 +1,137 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
+//                     Ingo Ruhnke <grumbel@gmx.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_FONT_H
+#define SUPERTUX_FONT_H
+
+#include <string>
+#include <stdint.h>
+
+#include "video/surface.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class Renderer;
+
+enum FontAlignment {
+  ALIGN_LEFT,
+  ALIGN_CENTER,
+  ALIGN_RIGHT
+};
+
+class Font
+{
+public:
+  enum GlyphWidth {
+    FIXED,
+    VARIABLE
+  };
+
+  /** Construct a fixed-width font
+   *
+   *  @param glyph_width  VARIABLE for proportional fonts, VARIABLE for monospace ones
+   *  @param filename     image file containing the characters
+   *  @param shadowfile   image file containing the characters shadows
+   *  @param char_width   width of a character
+   *  @param char_height  height of a character
+   */
+  Font(GlyphWidth glyph_width,
+       const std::string& filename, const std::string& shadowfile,
+       int char_width, int char_height, int shadowsize = 2);
+  ~Font();
+
+  /** returns the width of a given text. (Note that I won't add a normal
+   * get_width function here, as we might switch to variable width fonts in the
+   * future.)
+   * Supports breaklines.
+   */
+  float get_text_width(const std::string& text) const;
+
+  /** returns the height of a given text. This function supports breaklines.
+   * In case, you are positive that your text doesn't use break lines, you can
+   * just use get_height().
+   */
+  float get_text_height(const std::string& text) const;
+
+  /**
+   * returns the height of the font.
+   */
+  float get_height() const;
+
+  /**
+   * returns the given string, truncated (preferrably at whitespace) to be at most max_chars characters long
+   */
+  static std::string wrap_to_chars(const std::string& text, int max_chars, std::string* overflow);
+
+  /**
+   * returns the given string, truncated (preferrably at whitespace) to be at most "width" pixels wide
+   */
+  std::string wrap_to_width(const std::string& text, float width, std::string* overflow);
+
+  /** Draws the given text to the screen. Also needs the position.
+   * Type of alignment, drawing effect and alpha are optional. */
+  void draw(Renderer *renderer, const std::string& text, const Vector& pos,
+            FontAlignment allignment = ALIGN_LEFT,
+            DrawingEffect drawing_effect = NO_EFFECT,
+            float alpha = 1.0f) const;
+
+private:
+  friend class DrawingContext;
+
+  void draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+                 DrawingEffect drawing_effect = NO_EFFECT,
+                 float alpha = 1.0f) const;
+
+  void draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+                  const Vector& position, DrawingEffect drawing_effect,
+                  float alpha) const;
+
+  /** Convert a Unicode character code to the index of its glyph */
+  int chr2glyph(uint32_t chr) const;
+
+  GlyphWidth glyph_width;
+  Surface*   glyph_surface;
+  Surface*   shadow_glyph_surface;
+  int char_height;
+  int shadowsize;
+
+  /// the number of the first character that is represented in the font
+  uint32_t first_char;
+  /// the number of the last character that is represented in the font
+  uint32_t char_count;
+
+  struct Glyph {
+    /** How many pixels should the cursor advance after printing the
+        glyph */
+    float advance;
+
+    /** Offset that is used when drawing the glyph */
+    Vector offset;
+
+    /** Position of the glyph inside the surface */
+    Rect rect;
+  };
+
+  /** Location of the characters inside the surface */
+  std::vector<Glyph> glyphs;
+  std::vector<Glyph> shadow_glyphs;
+};
+
+#endif
diff --git a/src/video/gl_lightmap.cpp b/src/video/gl_lightmap.cpp
new file mode 100644 (file)
index 0000000..014fb73
--- /dev/null
@@ -0,0 +1,311 @@
+//  $Id: gl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <math.h>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "gl_lightmap.hpp"
+#include "gl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "renderer.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "gl_texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace
+{
+  inline void intern_draw(float left, float top, float right, float bottom,
+                                  float uv_left, float uv_top,
+                                  float uv_right, float uv_bottom,
+                                  float angle, float alpha,
+                                  const Color& color,
+                                  const Blend& blend,
+                                  DrawingEffect effect)
+  {
+    if(effect & HORIZONTAL_FLIP)
+      std::swap(uv_left, uv_right);
+    if(effect & VERTICAL_FLIP) {
+      std::swap(uv_top, uv_bottom);
+    }
+
+    float center_x = (left + right) / 2;
+    float center_y = (top + bottom) / 2;
+
+    float sa = sinf(angle/180.0f*M_PI);
+    float ca = cosf(angle/180.0f*M_PI);
+
+    left  -= center_x;
+    right -= center_x;
+
+    top    -= center_y;
+    bottom -= center_y;
+
+    glBlendFunc(blend.sfactor, blend.dfactor);
+    glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
+    glBegin(GL_QUADS);
+    glTexCoord2f(uv_left, uv_top);
+    glVertex2f(left*ca - top*sa + center_x,
+               left*sa + top*ca + center_y);
+
+    glTexCoord2f(uv_right, uv_top);
+    glVertex2f(right*ca - top*sa + center_x,
+               right*sa + top*ca + center_y);
+
+    glTexCoord2f(uv_right, uv_bottom);
+    glVertex2f(right*ca - bottom*sa + center_x,
+               right*sa + bottom*ca + center_y);
+
+    glTexCoord2f(uv_left, uv_bottom);
+    glVertex2f(left*ca - bottom*sa + center_x,
+               left*sa + bottom*ca + center_y);
+    glEnd();
+
+    // FIXME: find a better way to restore the blend mode
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  }
+}
+
+namespace GL
+{
+  static inline int next_po2(int val)
+  {
+    int result = 1;
+    while(result < val)
+      result *= 2;
+
+    return result;
+  }
+
+  Lightmap::Lightmap()
+  {
+    screen = SDL_GetVideoSurface();
+
+    lightmap_width = screen->w / LIGHTMAP_DIV;
+    lightmap_height = screen->h / LIGHTMAP_DIV;
+    unsigned int width = next_po2(lightmap_width);
+    unsigned int height = next_po2(lightmap_height);
+
+    lightmap = new Texture(width, height);
+
+    lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
+    lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
+    texture_manager->register_texture(lightmap);
+  }
+
+  Lightmap::~Lightmap()
+  {
+    texture_manager->remove_texture(lightmap);
+    delete lightmap;
+  }
+
+  void
+  Lightmap::start_draw(const Color &ambient_color)
+  {
+    glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+
+    glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 );
+    glClear(GL_COLOR_BUFFER_BIT);
+  }
+
+  void
+  Lightmap::end_draw()
+  {
+    glDisable(GL_BLEND);
+    glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
+
+    glViewport(0, 0, screen->w, screen->h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glEnable(GL_BLEND);
+    //glClear(GL_COLOR_BUFFER_BIT);
+  }
+
+  void
+  Lightmap::do_draw()
+  {
+    const Texture* texture = lightmap;
+
+    // multiple the lightmap with the framebuffer
+    glBlendFunc(GL_DST_COLOR, GL_ZERO);
+
+    glBindTexture(GL_TEXTURE_2D, texture->get_handle());
+    glBegin(GL_QUADS);
+
+    glTexCoord2f(0, lightmap_uv_bottom);
+    glVertex2f(0, 0);
+
+    glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
+    glVertex2f(SCREEN_WIDTH, 0);
+
+    glTexCoord2f(lightmap_uv_right, 0);
+    glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+    glTexCoord2f(0, 0);
+    glVertex2f(0, SCREEN_HEIGHT);
+
+    glEnd();
+
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  }
+
+  void
+  Lightmap::draw_surface(const DrawingRequest& request)
+  {
+    const Surface* surface = (const Surface*) request.request_data;
+    GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+    GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+    glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+    intern_draw(request.pos.x, request.pos.y,
+                request.pos.x + surface->get_width(),
+                request.pos.y + surface->get_height(),
+                surface_data->get_uv_left(),
+                surface_data->get_uv_top(),
+                surface_data->get_uv_right(),
+                surface_data->get_uv_bottom(),
+                request.angle,
+                request.alpha,
+                request.color,
+                request.blend,
+                request.drawing_effect);
+  }
+
+  void
+  Lightmap::draw_surface_part(const DrawingRequest& request)
+  {
+    const SurfacePartRequest* surfacepartrequest
+      = (SurfacePartRequest*) request.request_data;
+    const Surface *surface = surfacepartrequest->surface;
+    GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+    GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+    float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
+    float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
+
+    float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
+    float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
+    float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
+    float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
+
+    glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+    intern_draw(request.pos.x, request.pos.y,
+                request.pos.x + surfacepartrequest->size.x,
+                request.pos.y + surfacepartrequest->size.y,
+                uv_left,
+                uv_top,
+                uv_right,
+                uv_bottom,
+                0.0,
+                request.alpha,
+                Color(1.0, 1.0, 1.0),
+                Blend(),
+                request.drawing_effect);
+  }
+
+  void
+  Lightmap::draw_gradient(const DrawingRequest& request)
+  {
+    const GradientRequest* gradientrequest 
+      = (GradientRequest*) request.request_data;
+    const Color& top = gradientrequest->top;
+    const Color& bottom = gradientrequest->bottom;
+
+    glDisable(GL_TEXTURE_2D);
+    glBegin(GL_QUADS);
+    glColor4f(top.red, top.green, top.blue, top.alpha);
+    glVertex2f(0, 0);
+    glVertex2f(SCREEN_WIDTH, 0);
+    glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
+    glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+    glVertex2f(0, SCREEN_HEIGHT);
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+    glColor4f(1, 1, 1, 1);
+  }
+
+  void
+  Lightmap::draw_filled_rect(const DrawingRequest& request)
+  {
+    const FillRectRequest* fillrectrequest
+      = (FillRectRequest*) request.request_data;
+
+    float x = request.pos.x;
+    float y = request.pos.y;
+    float w = fillrectrequest->size.x;
+    float h = fillrectrequest->size.y;
+
+    glDisable(GL_TEXTURE_2D);
+    glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
+              fillrectrequest->color.blue, fillrectrequest->color.alpha);
+
+    glBegin(GL_QUADS);
+    glVertex2f(x, y);
+    glVertex2f(x+w, y);
+    glVertex2f(x+w, y+h);
+    glVertex2f(x, y+h);
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+    glColor4f(1, 1, 1, 1);
+  }
+
+  void
+  Lightmap::get_light(const DrawingRequest& request) const
+  {
+    const GetLightRequest* getlightrequest 
+      = (GetLightRequest*) request.request_data;
+
+    float pixels[3];
+    for( int i = 0; i<3; i++)
+      pixels[i] = 0.0f; //set to black
+
+    float posX = request.pos.x * lightmap_width / SCREEN_WIDTH;
+    float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT;
+    glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels);
+    *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]);
+    //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]);
+  }
+}
+
+#endif
diff --git a/src/video/gl_lightmap.hpp b/src/video/gl_lightmap.hpp
new file mode 100644 (file)
index 0000000..cb70709
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id: gl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef SUPERTUX_GL_LIGHTMAP_H
+#define SUPERTUX_GL_LIGHTMAP_H
+
+#include <SDL_video.h>
+
+#include "lightmap.hpp"
+
+struct DrawingRequest;
+
+namespace GL
+{
+  class Texture;
+  class Lightmap : public ::Lightmap
+  {
+  public:
+    Lightmap();
+    ~Lightmap();
+
+    void start_draw(const Color &ambient_color);
+    void end_draw();
+    void do_draw();
+    void draw_surface(const DrawingRequest& request);
+    void draw_surface_part(const DrawingRequest& request);
+    void draw_text(const DrawingRequest& request);
+    void draw_gradient(const DrawingRequest& request);
+    void draw_filled_rect(const DrawingRequest& request);
+    void get_light(const DrawingRequest& request) const;
+
+  private:
+    static const int LIGHTMAP_DIV = 5;
+
+    SDL_Surface* screen;
+    Texture* lightmap;
+    int lightmap_width, lightmap_height;
+    float lightmap_uv_right, lightmap_uv_bottom;
+  };
+}
+
+#endif
+
+#endif
diff --git a/src/video/gl_renderer.cpp b/src/video/gl_renderer.cpp
new file mode 100644 (file)
index 0000000..3eee8f1
--- /dev/null
@@ -0,0 +1,339 @@
+//  $Id: gl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <math.h>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "gl_renderer.hpp"
+#include "gl_texture.hpp"
+#include "gl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+#define LIGHTMAP_DIV 5
+
+namespace
+{
+  inline void intern_draw(float left, float top, float right, float bottom,
+                                  float uv_left, float uv_top,
+                                  float uv_right, float uv_bottom,
+                                  float angle, float alpha,
+                                  const Color& color,
+                                  const Blend& blend,
+                                  DrawingEffect effect)
+  {
+    if(effect & HORIZONTAL_FLIP)
+      std::swap(uv_left, uv_right);
+    if(effect & VERTICAL_FLIP) {
+      std::swap(uv_top, uv_bottom);
+    }
+
+    float center_x = (left + right) / 2;
+    float center_y = (top + bottom) / 2;
+
+    float sa = sinf(angle/180.0f*M_PI);
+    float ca = cosf(angle/180.0f*M_PI);
+
+    left  -= center_x;
+    right -= center_x;
+
+    top    -= center_y;
+    bottom -= center_y;
+
+    glBlendFunc(blend.sfactor, blend.dfactor);
+    glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
+    glBegin(GL_QUADS);
+    glTexCoord2f(uv_left, uv_top);
+    glVertex2f(left*ca - top*sa + center_x,
+               left*sa + top*ca + center_y);
+
+    glTexCoord2f(uv_right, uv_top);
+    glVertex2f(right*ca - top*sa + center_x,
+               right*sa + top*ca + center_y);
+
+    glTexCoord2f(uv_right, uv_bottom);
+    glVertex2f(right*ca - bottom*sa + center_x,
+               right*sa + bottom*ca + center_y);
+
+    glTexCoord2f(uv_left, uv_bottom);
+    glVertex2f(left*ca - bottom*sa + center_x,
+               left*sa + bottom*ca + center_y);
+    glEnd();
+
+    // FIXME: find a better way to restore the blend mode
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  }
+}
+
+namespace GL
+{
+  Renderer::Renderer()
+  {
+    if(texture_manager != 0)
+      texture_manager->save_textures();
+
+    if(config->try_vsync) {
+      /* we want vsync for smooth scrolling */
+    SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
+    }
+
+    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+
+    int flags = SDL_OPENGL;
+    if(config->use_fullscreen)
+      flags |= SDL_FULLSCREEN;
+    int width = config->screenwidth;
+    int height = config->screenheight;
+    int bpp = 0;
+
+    SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags);
+    if(screen == 0) {
+      std::stringstream msg;
+      msg << "Couldn't set video mode (" << width << "x" << height
+          << "-" << bpp << "bpp): " << SDL_GetError();
+      throw std::runtime_error(msg.str());
+    }
+
+    // setup opengl state and transform
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    glViewport(0, 0, screen->w, screen->h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    // logical resolution here not real monitor resolution
+    glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glTranslatef(0, 0, 0);
+
+    check_gl_error("Setting up view matrices");
+
+
+    if(texture_manager == 0)
+      texture_manager = new TextureManager();
+    else
+      texture_manager->reload_textures();
+  }
+
+  Renderer::~Renderer()
+  {
+  }
+
+  void
+  Renderer::draw_surface(const DrawingRequest& request)
+  {
+    const Surface* surface = (const Surface*) request.request_data;
+    GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+    GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+    glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+    intern_draw(request.pos.x, request.pos.y,
+                request.pos.x + surface->get_width(),
+                request.pos.y + surface->get_height(),
+                surface_data->get_uv_left(),
+                surface_data->get_uv_top(),
+                surface_data->get_uv_right(),
+                surface_data->get_uv_bottom(),
+                request.angle,
+                request.alpha,
+                request.color,
+                request.blend,
+                request.drawing_effect);
+  }
+
+  void
+  Renderer::draw_surface_part(const DrawingRequest& request)
+  {
+    const SurfacePartRequest* surfacepartrequest
+      = (SurfacePartRequest*) request.request_data;
+    const Surface *surface = surfacepartrequest->surface;
+    GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+    GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+    float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
+    float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
+
+    float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
+    float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
+    float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
+    float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
+
+    glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+    intern_draw(request.pos.x, request.pos.y,
+                request.pos.x + surfacepartrequest->size.x,
+                request.pos.y + surfacepartrequest->size.y,
+                uv_left,
+                uv_top,
+                uv_right,
+                uv_bottom,
+                0.0,
+                request.alpha,
+                Color(1.0, 1.0, 1.0),
+                Blend(),
+                request.drawing_effect);
+  }
+
+  void
+  Renderer::draw_gradient(const DrawingRequest& request)
+  {
+    const GradientRequest* gradientrequest 
+      = (GradientRequest*) request.request_data;
+    const Color& top = gradientrequest->top;
+    const Color& bottom = gradientrequest->bottom;
+
+    glDisable(GL_TEXTURE_2D);
+    glBegin(GL_QUADS);
+    glColor4f(top.red, top.green, top.blue, top.alpha);
+    glVertex2f(0, 0);
+    glVertex2f(SCREEN_WIDTH, 0);
+    glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
+    glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+    glVertex2f(0, SCREEN_HEIGHT);
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+    glColor4f(1, 1, 1, 1);
+  }
+
+  void
+  Renderer::draw_filled_rect(const DrawingRequest& request)
+  {
+    const FillRectRequest* fillrectrequest
+      = (FillRectRequest*) request.request_data;
+
+    float x = request.pos.x;
+    float y = request.pos.y;
+    float w = fillrectrequest->size.x;
+    float h = fillrectrequest->size.y;
+
+    glDisable(GL_TEXTURE_2D);
+    glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
+              fillrectrequest->color.blue, fillrectrequest->color.alpha);
+
+    glBegin(GL_QUADS);
+    glVertex2f(x, y);
+    glVertex2f(x+w, y);
+    glVertex2f(x+w, y+h);
+    glVertex2f(x, y+h);
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+    glColor4f(1, 1, 1, 1);
+  }
+
+  void 
+  Renderer::do_take_screenshot()
+  {
+    // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
+
+    SDL_Surface *shot_surf;
+    // create surface to hold screenshot
+    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+    shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
+    #else
+    shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+    #endif
+    if (!shot_surf) {
+      log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
+      return;
+    }
+
+    // read pixels into array
+    char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
+    if (!pixels) {
+      log_warning << "Could not allocate memory to store screenshot" << std::endl;
+      SDL_FreeSurface(shot_surf);
+      return;
+    }
+    glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+
+    // copy array line-by-line
+    for (int i = 0; i < SCREEN_HEIGHT; i++) {
+      char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
+      if(SDL_MUSTLOCK(shot_surf))
+      {
+        SDL_LockSurface(shot_surf);
+      }
+      char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
+      memcpy(dst, src, 3 * SCREEN_WIDTH);
+      if(SDL_MUSTLOCK(shot_surf))
+      {
+        SDL_UnlockSurface(shot_surf);
+      }
+    }
+
+    // free array
+    delete[](pixels);
+
+    // save screenshot
+    static const std::string writeDir = PHYSFS_getWriteDir();
+    static const std::string dirSep = PHYSFS_getDirSeparator();
+    static const std::string baseName = "screenshot";
+    static const std::string fileExt = ".bmp";
+    std::string fullFilename;
+    for (int num = 0; num < 1000; num++) {
+      std::ostringstream oss;
+      oss << baseName;
+      oss << std::setw(3) << std::setfill('0') << num;
+      oss << fileExt;
+      std::string fileName = oss.str();
+      fullFilename = writeDir + dirSep + fileName;
+      if (!PHYSFS_exists(fileName.c_str())) {
+        SDL_SaveBMP(shot_surf, fullFilename.c_str());
+        log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
+        SDL_FreeSurface(shot_surf);
+        return;
+      }
+    }
+    log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
+    SDL_FreeSurface(shot_surf);
+  }
+
+  void
+  Renderer::flip()
+  {
+    assert_gl("drawing");
+    SDL_GL_SwapBuffers();
+  }
+}
+
+#endif
diff --git a/src/video/gl_renderer.hpp b/src/video/gl_renderer.hpp
new file mode 100644 (file)
index 0000000..1eef6b4
--- /dev/null
@@ -0,0 +1,48 @@
+//  $Id: gl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef SUPERTUX_GL_RENDERER_H
+#define SUPERTUX_GL_RENDERER_H
+
+#include "renderer.hpp"
+
+namespace GL
+{
+  class Renderer : public ::Renderer
+  {
+  public:
+    Renderer();
+    ~Renderer();
+
+    void draw_surface(const DrawingRequest& request);
+    void draw_surface_part(const DrawingRequest& request);
+    void draw_text(const DrawingRequest& request);
+    void draw_gradient(const DrawingRequest& request);
+    void draw_filled_rect(const DrawingRequest& request);
+    void do_take_screenshot();
+    void flip();
+  };
+}
+
+#endif
+
+#endif
diff --git a/src/video/gl_surface_data.hpp b/src/video/gl_surface_data.hpp
new file mode 100644 (file)
index 0000000..a441f10
--- /dev/null
@@ -0,0 +1,73 @@
+//  $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef __GL_SURFACE_DATA_HPP__
+#define __GL_SURFACE_DATA_HPP__
+
+#include "surface.hpp"
+
+namespace GL
+{
+  class SurfaceData
+  {
+  private:
+    const Surface &surface;
+    float uv_left;
+    float uv_top;
+    float uv_right;
+    float uv_bottom;
+
+  public:
+    SurfaceData(const Surface &surface) :
+      surface(surface)
+    {
+      uv_left = (float) surface.get_x() / surface.get_texture()->get_texture_width();
+      uv_top = (float) surface.get_y() / surface.get_texture()->get_texture_height();
+      uv_right = (float) (surface.get_x() + surface.get_width()) / surface.get_texture()->get_texture_width();
+      uv_bottom = (float) (surface.get_y() + surface.get_height()) / surface.get_texture()->get_texture_height();
+    }
+
+    float get_uv_left() const
+    {
+      return surface.get_flipx() ? uv_right : uv_left;
+    }
+
+    float get_uv_top() const
+    {
+      return uv_top;
+    }
+
+    float get_uv_right() const
+    {
+      return surface.get_flipx() ? uv_left : uv_right;
+    }
+
+    float get_uv_bottom() const
+    {
+      return uv_bottom;
+    }
+  };
+}
+
+#endif
+
+#endif
diff --git a/src/video/gl_texture.cpp b/src/video/gl_texture.cpp
new file mode 100644 (file)
index 0000000..413d0ce
--- /dev/null
@@ -0,0 +1,154 @@
+//  $Id: gl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include "gl_texture.hpp"
+#include "gameconfig.hpp"
+#include "glutil.hpp"
+#include "log.hpp"
+
+#include <assert.h>
+#include <stdexcept>
+
+namespace
+{
+  inline bool is_power_of_2(int v)
+  {
+    return (v & (v-1)) == 0;
+  }
+
+  inline int next_power_of_two(int val)
+  {
+    int result = 1;
+    while(result < val)
+      result *= 2;
+    return result;
+  }
+}
+
+namespace GL
+{
+  Texture::Texture(unsigned int width, unsigned int height)
+  {
+    assert(is_power_of_2(width));
+    assert(is_power_of_2(height));
+    texture_width = width;
+    texture_height = height;
+    image_width = width;
+    image_height = height;
+
+    assert_gl("before creating texture");
+    glGenTextures(1, &handle);
+
+    try {
+      glBindTexture(GL_TEXTURE_2D, handle);
+
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
+                   texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+      set_texture_params();
+    } catch(...) {
+      glDeleteTextures(1, &handle);
+      throw;
+    }
+  }
+
+  Texture::Texture(SDL_Surface* image)
+  {
+    texture_width = next_power_of_two(image->w);
+    texture_height = next_power_of_two(image->h);
+    image_width = image->w;
+    image_height = image->h;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+    SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
+        texture_width, texture_height, 32,
+        0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#else
+    SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
+        texture_width, texture_height, 32,
+        0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#endif
+
+    if(convert == 0) {
+      throw std::runtime_error("Couldn't create texture: out of memory");
+    }
+
+    SDL_SetAlpha(image, 0, 0);
+    SDL_BlitSurface(image, 0, convert, 0);
+
+    assert_gl("before creating texture");
+    glGenTextures(1, &handle);
+
+    try {
+      GLenum sdl_format;
+      if(convert->format->BytesPerPixel == 3)
+        sdl_format = GL_RGB;
+      else if(convert->format->BytesPerPixel == 4)
+        sdl_format = GL_RGBA;
+      else
+        assert(false);
+
+      glBindTexture(GL_TEXTURE_2D, handle);
+      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+      glPixelStorei(GL_UNPACK_ROW_LENGTH, convert->pitch/convert->format->BytesPerPixel);
+      if(SDL_MUSTLOCK(convert))
+      {
+        SDL_LockSurface(convert);
+      }
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
+              texture_height, 0, sdl_format,
+              GL_UNSIGNED_BYTE, convert->pixels);
+      if(SDL_MUSTLOCK(convert))
+      {
+        SDL_UnlockSurface(convert);
+      }
+
+      assert_gl("creating texture");
+
+      set_texture_params();
+    } catch(...) {
+      glDeleteTextures(1, &handle);
+      SDL_FreeSurface(convert);
+      throw;
+    }
+    SDL_FreeSurface(convert);
+  }
+
+  Texture::~Texture()
+  {
+    glDeleteTextures(1, &handle);
+  }
+
+  void
+  Texture::set_texture_params()
+  {
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+    assert_gl("set texture params");
+  }
+}
+
+#endif
diff --git a/src/video/gl_texture.hpp b/src/video/gl_texture.hpp
new file mode 100644 (file)
index 0000000..14532cc
--- /dev/null
@@ -0,0 +1,97 @@
+//  $Id: gl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef __GL_TEXTURE_HPP__
+#define __GL_TEXTURE_HPP__
+
+#include <SDL.h>
+
+#include "texture.hpp"
+#include "glutil.hpp"
+
+/**
+ * This class is a wrapper around a texture handle. It stores the texture width
+ * and height and provides convenience functions for uploading SDL_Surfaces
+ * into the texture
+ */
+namespace GL
+{
+  class Texture : public ::Texture
+  {
+  protected:
+    GLuint handle;
+    unsigned int texture_width;
+    unsigned int texture_height;
+    unsigned int image_width;
+    unsigned int image_height;
+
+  public:
+    Texture(unsigned int width, unsigned int height);
+    Texture(SDL_Surface* image);
+    ~Texture();
+
+    const GLuint &get_handle() const {
+      return handle;
+    }
+
+    void set_handle(GLuint handle) {
+      this->handle = handle;
+    }
+
+    unsigned int get_texture_width() const
+    {
+      return texture_width;
+    }
+
+    unsigned int get_texture_height() const
+    {
+      return texture_height;
+    }
+
+    unsigned int get_image_width() const
+    {
+      return image_width;
+    }
+
+    unsigned int get_image_height() const
+    {
+      return image_height;
+    }
+
+    void set_image_width(unsigned int width)
+    {
+      image_width = width;
+    }
+
+    void set_image_height(unsigned int height)
+    {
+      image_height = height;
+    }
+
+  private:
+    void set_texture_params();
+  };
+}
+
+#endif
+
+#endif
diff --git a/src/video/glutil.hpp b/src/video/glutil.hpp
new file mode 100644 (file)
index 0000000..85200bd
--- /dev/null
@@ -0,0 +1,102 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __GLUTIL_HPP__
+#define __GLUTIL_HPP__
+
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <sstream>
+#include <stdexcept>
+
+#ifndef MACOSX
+#include <GL/gl.h>
+#include <GL/glext.h>
+#else
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif
+
+static inline void check_gl_error(const char* message)
+{
+#ifdef DEBUG
+  GLenum error = glGetError();
+  if(error != GL_NO_ERROR) {
+    std::ostringstream msg;
+    msg << "OpenGLError while '" << message << "': ";
+    switch(error) {
+      case GL_INVALID_ENUM:
+        msg << "INVALID_ENUM: An unacceptable value is specified for an "
+               "enumerated argument.";
+        break;
+      case GL_INVALID_VALUE:
+        msg << "INVALID_VALUE: A numeric argument is out of range.";
+        break;
+      case GL_INVALID_OPERATION:
+        msg << "INVALID_OPERATION: The specified operation is not allowed "
+               "in the current state.";
+        break;
+      case GL_STACK_OVERFLOW:
+        msg << "STACK_OVERFLOW: This command would cause a stack overflow.";
+        break;
+      case GL_STACK_UNDERFLOW:
+        msg << "STACK_UNDERFLOW: This command would cause a stack underflow.";
+        break;
+      case GL_OUT_OF_MEMORY:
+        msg << "OUT_OF_MEMORY: There is not enough memory left to execute the "
+               "command.";
+        break;
+#ifdef GL_TABLE_TOO_LARGE
+      case GL_TABLE_TOO_LARGE:
+        msg << "TABLE_TOO_LARGE: table is too large";
+        break;
+#endif
+      default:
+        msg << "Unknown error (code " << error << ")";
+    }
+
+    throw std::runtime_error(msg.str());
+  }
+#else
+  (void) message;
+#endif
+}
+
+static inline void assert_gl(const char* message)
+{
+#ifdef DEBUG
+  check_gl_error(message);
+#else
+  (void) message;
+#endif
+}
+
+#else
+
+#define GLenum int
+#define GLint int
+#define GL_SRC_ALPHA 0
+#define GL_ONE_MINUS_SRC_ALPHA 1
+#define GL_RGBA 2
+#define GL_ONE 3
+
+#endif
+
+#endif
diff --git a/src/video/lightmap.hpp b/src/video/lightmap.hpp
new file mode 100644 (file)
index 0000000..049b21e
--- /dev/null
@@ -0,0 +1,58 @@
+//  $Id: lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_LIGHTMAP_H
+#define SUPERTUX_LIGHTMAP_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Texture;
+struct DrawingRequest;
+
+class Lightmap
+{
+public:
+  virtual ~Lightmap() {}
+
+  virtual void start_draw(const Color &ambient_color) = 0;
+  virtual void end_draw() = 0;
+  virtual void do_draw() = 0;
+  virtual void draw_surface(const DrawingRequest& request) = 0;
+  virtual void draw_surface_part(const DrawingRequest& request) = 0;
+  virtual void draw_gradient(const DrawingRequest& request) = 0;
+  virtual void draw_filled_rect(const DrawingRequest& request) = 0;
+  virtual void get_light(const DrawingRequest& request) const = 0;
+};
+
+#endif
+
diff --git a/src/video/renderer.hpp b/src/video/renderer.hpp
new file mode 100644 (file)
index 0000000..2bd96d7
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id: drawing_context.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_RENDERER_H
+#define SUPERTUX_RENDERER_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Surface;
+class Texture;
+struct DrawingRequest;
+
+class Renderer
+{
+public:
+  virtual ~Renderer() {}
+
+  virtual void draw_surface(const DrawingRequest& request) = 0;
+  virtual void draw_surface_part(const DrawingRequest& request) = 0;
+  virtual void draw_gradient(const DrawingRequest& request) = 0;
+  virtual void draw_filled_rect(const DrawingRequest& request)= 0;
+  virtual void do_take_screenshot() = 0;
+  virtual void flip() = 0;
+};
+
+#endif
+
diff --git a/src/video/sdl_lightmap.cpp b/src/video/sdl_lightmap.cpp
new file mode 100644 (file)
index 0000000..8e7c70f
--- /dev/null
@@ -0,0 +1,614 @@
+//  $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "sdl_lightmap.hpp"
+#include "sdl_texture.hpp"
+#include "sdl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "renderer.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace SDL
+{
+  Lightmap::Lightmap()
+  {
+    screen = SDL_GetVideoSurface();
+
+    float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+    float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+    if(xfactor < yfactor)
+    {
+      numerator = config->screenwidth;
+      denominator = SCREEN_WIDTH;
+    }
+    else
+    {
+      numerator = config->screenheight;
+      denominator = SCREEN_HEIGHT;
+    }
+
+    LIGHTMAP_DIV = 8 * numerator / denominator;
+
+    width = screen->w / LIGHTMAP_DIV;
+    height = screen->h / LIGHTMAP_DIV;
+
+    red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+    green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+    blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+  }
+
+  Lightmap::~Lightmap()
+  {
+    free(red_channel);
+    free(green_channel);
+    free(blue_channel);
+  }
+
+  void
+  Lightmap::start_draw(const Color &ambient_color)
+  {
+    memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
+    memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
+    memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
+  }
+
+  void
+  Lightmap::end_draw()
+  {
+  }
+
+//#define BILINEAR
+
+#ifdef BILINEAR
+  namespace
+  {
+    void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
+    {
+      color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
+      color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
+      color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
+    }
+  }
+#endif
+
+  void
+  Lightmap::do_draw()
+  {
+    // FIXME: This is really slow
+    if(LIGHTMAP_DIV == 1)
+    {
+      int bpp = screen->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_LockSurface(screen);
+      }
+      Uint8 *pixel = (Uint8 *) screen->pixels;
+      int loc = 0;
+      for(int y = 0;y < height;y++) {
+        for(int x = 0;x < width;x++, pixel += bpp, loc++) {
+          if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+          {
+            continue;
+          }
+          Uint32 mapped = 0;
+          switch(bpp) {
+            case 1:
+              mapped = *pixel;
+              break;
+            case 2:
+              mapped = *(Uint16 *)pixel;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              mapped |= pixel[0] << 16;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 0;
+#else
+              mapped |= pixel[0] << 0;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 16;
+#endif
+              break;
+            case 4:
+              mapped = *(Uint32 *)pixel;
+              break;
+          }
+          Uint8 red, green, blue, alpha;
+          SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+          red = (red * red_channel[loc]) >> 8;
+          green = (green * green_channel[loc]) >> 8;
+          blue = (blue * blue_channel[loc]) >> 8;
+          mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+          switch(bpp) {
+            case 1:
+              *pixel = mapped;
+              break;
+            case 2:
+              *(Uint16 *)pixel = mapped;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              pixel[0] = (mapped >> 16) & 0xff;
+              pixel[1] = (mapped >> 8) & 0xff;
+              pixel[2] = (mapped >> 0) & 0xff;
+#else
+              pixel[0] = (mapped >> 0) & 0xff;
+              pixel[1] = (mapped >> 8) & 0xff;
+              pixel[2] = (mapped >> 16) & 0xff;
+#endif
+              break;
+            case 4:
+              *(Uint32 *)pixel = mapped;
+              break;
+          }
+        }
+        pixel += screen->pitch - width * bpp;
+      }
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_UnlockSurface(screen);
+      }
+    }
+    else
+    {
+      int bpp = screen->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_LockSurface(screen);
+      }
+      Uint8 *div_pixel = (Uint8 *) screen->pixels;
+      int loc = 0;
+      for(int y = 0;y < height;y++) {
+        for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
+          if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+          {
+            continue;
+          }
+          Uint8 *pixel = div_pixel;
+          for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
+            for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
+              Uint32 mapped = 0;
+              switch(bpp) {
+                case 1:
+                  mapped = *pixel;
+                  break;
+                case 2:
+                  mapped = *(Uint16 *)pixel;
+                  break;
+                case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+                  mapped |= pixel[0] << 16;
+                  mapped |= pixel[1] << 8;
+                  mapped |= pixel[2] << 0;
+#else
+                  mapped |= pixel[0] << 0;
+                  mapped |= pixel[1] << 8;
+                  mapped |= pixel[2] << 16;
+#endif
+                  break;
+                case 4:
+                  mapped = *(Uint32 *)pixel;
+                  break;
+              }
+              Uint8 red, green, blue, alpha;
+              SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+
+#ifdef BILINEAR
+              int xinc = (x + 1 != width ? 1 : 0);
+              int yinc = (y + 1 != height ? width : 0);
+              Uint8 color00[3], color01[3], color10[3], color11[3];
+              {
+                color00[0] = red_channel[loc];
+                color00[1] = green_channel[loc];
+                color00[2] = blue_channel[loc];
+              }
+              {
+                color01[0] = red_channel[loc + xinc];
+                color01[1] = green_channel[loc + xinc];
+                color01[2] = blue_channel[loc + xinc];
+              }
+              {
+                color10[0] = red_channel[loc + yinc];
+                color10[1] = green_channel[loc + yinc];
+                color10[2] = blue_channel[loc + yinc];
+              }
+              {
+                color11[0] = red_channel[loc + yinc + xinc];
+                color11[1] = green_channel[loc + yinc + xinc];
+                color11[2] = blue_channel[loc + yinc + xinc];
+              }
+              Uint8 color0[3], color1[3], color[3];
+              merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
+              merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
+              merge(color, color0, color1, div_y, LIGHTMAP_DIV);
+              red = (red * color[0]) >> 8;
+              green = (green * color[1]) >> 8;
+              blue = (blue * color[2]) >> 8;
+#else
+              red = (red * red_channel[loc]) >> 8;
+              green = (green * green_channel[loc]) >> 8;
+              blue = (blue * blue_channel[loc]) >> 8;
+#endif
+
+              mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+              switch(bpp) {
+                case 1:
+                  *pixel = mapped;
+                  break;
+                case 2:
+                  *(Uint16 *)pixel = mapped;
+                  break;
+                case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+                  pixel[0] = (mapped >> 16) & 0xff;
+                  pixel[1] = (mapped >> 8) & 0xff;
+                  pixel[2] = (mapped >> 0) & 0xff;
+#else
+                  pixel[0] = (mapped >> 0) & 0xff;
+                  pixel[1] = (mapped >> 8) & 0xff;
+                  pixel[2] = (mapped >> 16) & 0xff;
+#endif
+                  break;
+                case 4:
+                  *(Uint32 *)pixel = mapped;
+                  break;
+              }
+            }
+            pixel += screen->pitch - LIGHTMAP_DIV * bpp;
+          }
+        }
+        div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
+      }
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_UnlockSurface(screen);
+      }
+    }
+  }
+
+  void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
+  {
+    dstx /= LIGHTMAP_DIV;
+    dsty /= LIGHTMAP_DIV;
+    int srcx = src_rect->x / LIGHTMAP_DIV;
+    int srcy = src_rect->y / LIGHTMAP_DIV;
+    int blit_width = src_rect->w / LIGHTMAP_DIV;
+    int blit_height = src_rect->h / LIGHTMAP_DIV;
+    int bpp = src->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+    int loc = dsty * width + dstx;
+    for(int y = 0;y < blit_height;y++) {
+      for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
+        if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
+        {
+          continue;
+        }
+        if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+        {
+          continue;
+        }
+
+        Uint32 mapped = 0;
+        switch(bpp) {
+          case 1:
+            mapped = *pixel;
+            break;
+          case 2:
+            mapped = *(Uint16 *)pixel;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            mapped |= pixel[0] << 16;
+            mapped |= pixel[1] << 8;
+            mapped |= pixel[2] << 0;
+#else
+            mapped |= pixel[0] << 0;
+            mapped |= pixel[1] << 8;
+            mapped |= pixel[2] << 16;
+#endif
+            break;
+          case 4:
+            mapped = *(Uint32 *)pixel;
+            break;
+        }
+        Uint8 red, green, blue, alpha;
+        SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + (red * alpha >> 8);
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + (green * alpha >> 8);
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
+      }
+      pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
+      loc += width - blit_width;
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+  }
+
+  /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
+  {
+    int bpp = src->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
+    int loc = dsty * width + dstx;
+    for(int y = 0;y < src_rect->h;y++) {
+      for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
+        if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
+        {
+          continue;
+        }
+        if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+        {
+          continue;
+        }
+
+        Uint32 mapped = 0;
+        switch(bpp) {
+          case 1:
+            mapped = *pixel;
+            break;
+          case 2:
+            mapped = *(Uint16 *)pixel;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            mapped |= pixel[0] << 16;
+            mapped |= pixel[1] << 8;
+            mapped |= pixel[2] << 0;
+#else
+            mapped |= pixel[0] << 0;
+            mapped |= pixel[1] << 8;
+            mapped |= pixel[2] << 16;
+#endif
+            break;
+          case 4:
+            mapped = *(Uint32 *)pixel;
+            break;
+        }
+        Uint8 red, green, blue, alpha;
+        SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + (red * alpha >> 8);
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + (green * alpha >> 8);
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
+      }
+      pixel += src->pitch - src_rect->w * bpp;
+      loc += width - src_rect->w;
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+  }*/
+
+  void
+  Lightmap::draw_surface(const DrawingRequest& request)
+  {
+    if((request.color.red == 0.0 && request.color.green == 0.0 && request.color.blue == 0.0) || request.color.alpha == 0.0 || request.alpha == 0.0)
+    {
+      return;
+    }
+    //FIXME: support parameters request.alpha, request.angle, request.blend
+    const Surface* surface = (const Surface*) request.request_data;
+    SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+    SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
+
+    DrawingEffect effect = request.drawing_effect;
+    if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+    SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
+
+    // get and check SDL_Surface
+    if (transform == 0) {
+      std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+      return;
+    }  
+
+    SDL_Rect *src_rect = surface_data->get_src_rect(effect);
+    int dstx = (int) request.pos.x * numerator / denominator;
+    int dsty = (int) request.pos.y * numerator / denominator;
+    light_blit(transform, src_rect, dstx, dsty);
+  }
+
+  void
+  Lightmap::draw_surface_part(const DrawingRequest& request)
+  {
+    const SurfacePartRequest* surfacepartrequest
+      = (SurfacePartRequest*) request.request_data;
+
+    const Surface* surface = surfacepartrequest->surface;
+    SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+
+    DrawingEffect effect = request.drawing_effect;
+    if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+    SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
+
+    // get and check SDL_Surface
+    if (transform == 0) {
+      std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+      return;
+    }  
+
+    int ox, oy;
+    if (effect == HORIZONTAL_FLIP)
+    {
+      ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
+    }
+    else
+    {
+      ox = surface->get_x();
+    }
+    if (effect == VERTICAL_FLIP)
+    {
+      oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
+    }
+    else
+    {
+      oy = surface->get_y();
+    }
+
+    SDL_Rect src_rect;
+    src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
+    src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
+    src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
+    src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
+    int dstx = (int) request.pos.x * numerator / denominator;
+    int dsty = (int) request.pos.y * numerator / denominator;
+    light_blit(transform, &src_rect, dstx, dsty);
+  }
+
+  void
+  Lightmap::draw_gradient(const DrawingRequest& request)
+  {
+    const GradientRequest* gradientrequest 
+      = (GradientRequest*) request.request_data;
+    const Color& top = gradientrequest->top;
+    const Color& bottom = gradientrequest->bottom;
+
+    int loc = 0;
+    for(int y = 0;y < height;++y)
+    {
+      Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
+      Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
+      Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
+      Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
+      for(int x = 0;x < width;x++, loc++) {
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + (red * alpha >> 8);
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + (green * alpha >> 8);
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
+      }
+    }
+  }
+
+  void
+  Lightmap::draw_filled_rect(const DrawingRequest& request)
+  {
+    const FillRectRequest* fillrectrequest
+      = (FillRectRequest*) request.request_data;
+
+    int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
+    int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
+    int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
+    int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
+    Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
+    Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
+    Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
+    if(red == 0 && green == 0 && blue == 0)
+    {
+      return;
+    }
+    for(int y = rect_y;y < rect_y + rect_h;y++) {
+      for(int x = rect_x;x < rect_x + rect_w;x++) {
+        int loc = y * width + x;
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + red;
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + green;
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + blue;
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
+      }
+    }
+  }
+
+  void
+  Lightmap::get_light(const DrawingRequest& request) const
+  {
+    const GetLightRequest* getlightrequest 
+      = (GetLightRequest*) request.request_data;
+
+    int x = (int) (request.pos.x * width / SCREEN_WIDTH);
+    int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
+    int loc = y * width + x;
+    *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
+  }
+}
diff --git a/src/video/sdl_lightmap.hpp b/src/video/sdl_lightmap.hpp
new file mode 100644 (file)
index 0000000..fbde750
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id: sdl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_SDL_LIGHTMAP_H
+#define SUPERTUX_SDL_LIGHTMAP_H
+
+#include <SDL_video.h>
+
+#include "lightmap.hpp"
+
+class Color;
+struct DrawingRequest;
+
+namespace SDL
+{
+  class Lightmap : public ::Lightmap
+  {
+  public:
+    Lightmap();
+    ~Lightmap();
+
+    void start_draw(const Color &ambient_color);
+    void end_draw();
+    void do_draw();
+    void draw_surface(const DrawingRequest& request);
+    void draw_surface_part(const DrawingRequest& request);
+    void draw_text(const DrawingRequest& request);
+    void draw_gradient(const DrawingRequest& request);
+    void draw_filled_rect(const DrawingRequest& request);
+    void get_light(const DrawingRequest& request) const;
+
+  private:
+    SDL_Surface* screen;
+    Uint8 *red_channel;
+    Uint8 *blue_channel;
+    Uint8 *green_channel;
+    int width, height;
+    int numerator, denominator;
+    int LIGHTMAP_DIV;
+
+    void light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty);
+  };
+}
+
+#endif
+
diff --git a/src/video/sdl_renderer.cpp b/src/video/sdl_renderer.cpp
new file mode 100644 (file)
index 0000000..dede6c8
--- /dev/null
@@ -0,0 +1,433 @@
+//  $Id: sdl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "sdl_renderer.hpp"
+#include "sdl_texture.hpp"
+#include "sdl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "log.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace
+{
+  SDL_Surface *apply_alpha(SDL_Surface *src, float alpha_factor)
+  {
+    // FIXME: This is really slow
+    assert(src->format->Amask);
+    int alpha = (int) (alpha_factor * 256);
+    SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+    int bpp = dst->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_LockSurface(dst);
+    }
+    for(int y = 0;y < dst->h;y++) {
+      for(int x = 0;x < dst->w;x++) {
+        Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+        Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+        Uint32 mapped = 0;
+        switch(bpp) {
+          case 1:
+            mapped = *srcpixel;
+            break;
+          case 2:
+            mapped = *(Uint16 *)srcpixel;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            mapped |= srcpixel[0] << 16;
+            mapped |= srcpixel[1] << 8;
+            mapped |= srcpixel[2] << 0;
+#else
+            mapped |= srcpixel[0] << 0;
+            mapped |= srcpixel[1] << 8;
+            mapped |= srcpixel[2] << 16;
+#endif
+            break;
+          case 4:
+            mapped = *(Uint32 *)srcpixel;
+            break;
+        }
+        Uint8 r, g, b, a;
+        SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
+        mapped = SDL_MapRGBA(dst->format, r, g, b, (a * alpha) >> 8);
+        switch(bpp) {
+          case 1:
+            *dstpixel = mapped;
+            break;
+          case 2:
+            *(Uint16 *)dstpixel = mapped;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            dstpixel[0] = (mapped >> 16) & 0xff;
+            dstpixel[1] = (mapped >> 8) & 0xff;
+            dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+            dstpixel[0] = (mapped >> 0) & 0xff;
+            dstpixel[1] = (mapped >> 8) & 0xff;
+            dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+            break;
+          case 4:
+            *(Uint32 *)dstpixel = mapped;
+            break;
+        }
+      }
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_UnlockSurface(dst);
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+    return dst;
+  }
+}
+
+namespace SDL
+{
+  Renderer::Renderer()
+  {
+    const SDL_VideoInfo *info = SDL_GetVideoInfo();
+    log_info << "Hardware surfaces are " << (info->hw_available ? "" : "not ") << "available." << std::endl;
+    log_info << "Hardware to hardware blits are " << (info->blit_hw ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Hardware to hardware blits with colorkey are " << (info->blit_hw_CC ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Hardware to hardware blits with alpha are " << (info->blit_hw_A ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Software to hardware blits are " << (info->blit_sw ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Software to hardware blits with colorkey are " << (info->blit_sw_CC ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Software to hardware blits with alpha are " << (info->blit_sw_A ? "" : "not ") << "accelerated." << std::endl;
+    log_info << "Color fills are " << (info->blit_fill ? "" : "not ") << "accelerated." << std::endl;
+
+    int flags = SDL_SWSURFACE | SDL_ANYFORMAT;
+    if(config->use_fullscreen)
+      flags |= SDL_FULLSCREEN;
+    int width = config->screenwidth;
+    int height = config->screenheight;
+
+    screen = SDL_SetVideoMode(width, height, 0, flags);
+    if(screen == 0) {
+      std::stringstream msg;
+      msg << "Couldn't set video mode (" << width << "x" << height
+          << "): " << SDL_GetError();
+      throw std::runtime_error(msg.str());
+    }
+
+    float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+    float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+    if(xfactor < yfactor)
+    {
+      numerator = config->screenwidth;
+      denominator = SCREEN_WIDTH;
+    }
+    else
+    {
+      numerator = config->screenheight;
+      denominator = SCREEN_HEIGHT;
+    }
+
+    if(texture_manager == 0)
+      texture_manager = new TextureManager();
+  }
+
+  Renderer::~Renderer()
+  {
+  }
+
+  void
+  Renderer::draw_surface(const DrawingRequest& request)
+  {
+    //FIXME: support parameters request.alpha, request.angle, request.blend
+    const Surface* surface = (const Surface*) request.request_data;
+    SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+    SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
+
+    DrawingEffect effect = request.drawing_effect;
+    if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+    SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
+
+    // get and check SDL_Surface
+    if (transform == 0) {
+      std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+      return;
+    }  
+
+    SDL_Rect *src_rect = surface_data->get_src_rect(effect);
+    SDL_Rect dst_rect;
+    dst_rect.x = (int) request.pos.x * numerator / denominator;
+    dst_rect.y = (int) request.pos.y * numerator / denominator;
+
+    Uint8 alpha = 0;
+    if(request.alpha != 1.0)
+    {
+      if(!transform->format->Amask)
+      {
+        if(transform->flags & SDL_SRCALPHA)
+        {
+          alpha = transform->format->alpha;
+        }
+        else
+        {
+          alpha = 255;
+        }
+        SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
+      }
+      /*else
+      {
+        transform = apply_alpha(transform, request.alpha);
+      }*/
+    }
+
+    SDL_BlitSurface(transform, src_rect, screen, &dst_rect);
+
+    if(request.alpha != 1.0)
+    {
+      if(!transform->format->Amask)
+      {
+        if(alpha == 255)
+        {
+          SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
+        }
+        else
+        {
+          SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+        }
+      }
+      /*else
+      {
+        SDL_FreeSurface(transform);
+      }*/
+    }
+  }
+
+  void
+  Renderer::draw_surface_part(const DrawingRequest& request)
+  {
+    const SurfacePartRequest* surfacepartrequest
+      = (SurfacePartRequest*) request.request_data;
+
+    const Surface* surface = surfacepartrequest->surface;
+    SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+
+    DrawingEffect effect = request.drawing_effect;
+    if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+    SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
+
+    // get and check SDL_Surface
+    if (transform == 0) {
+      std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+      return;
+    }  
+
+    int ox, oy;
+    if (effect == HORIZONTAL_FLIP)
+    {
+      ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
+    }
+    else
+    {
+      ox = surface->get_x();
+    }
+    if (effect == VERTICAL_FLIP)
+    {
+      oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
+    }
+    else
+    {
+      oy = surface->get_y();
+    }
+
+    SDL_Rect src_rect;
+    src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
+    src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
+    src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
+    src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
+
+    SDL_Rect dst_rect;
+    dst_rect.x = (int) request.pos.x * numerator / denominator;
+    dst_rect.y = (int) request.pos.y * numerator / denominator;
+
+    Uint8 alpha = 0;
+    if(request.alpha != 1.0)
+    {
+      if(!transform->format->Amask)
+      {
+        if(transform->flags & SDL_SRCALPHA)
+        {
+          alpha = transform->format->alpha;
+        }
+        else
+        {
+          alpha = 255;
+        }
+        SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
+      }
+      /*else
+      {
+        transform = apply_alpha(transform, request.alpha);
+      }*/
+    }
+
+    SDL_BlitSurface(transform, &src_rect, screen, &dst_rect);
+
+    if(request.alpha != 1.0)
+    {
+      if(!transform->format->Amask)
+      {
+        if(alpha == 255)
+        {
+          SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
+        }
+        else
+        {
+          SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+        }
+      }
+      /*else
+      {
+        SDL_FreeSurface(transform);
+      }*/
+    }
+  }
+
+  void
+  Renderer::draw_gradient(const DrawingRequest& request)
+  {
+    const GradientRequest* gradientrequest 
+      = (GradientRequest*) request.request_data;
+    const Color& top = gradientrequest->top;
+    const Color& bottom = gradientrequest->bottom;
+
+    for(int y = 0;y < screen->h;++y)
+    {
+      Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255);
+      Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255);
+      Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255);
+      Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255);
+      Uint32 color = SDL_MapRGB(screen->format, r, g, b);
+
+      SDL_Rect rect;
+      rect.x = 0;
+      rect.y = y;
+      rect.w = screen->w;
+      rect.h = 1;
+
+      if(a == SDL_ALPHA_OPAQUE) {
+        SDL_FillRect(screen, &rect, color);
+      } else if(a != SDL_ALPHA_TRANSPARENT) {
+        SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+
+        SDL_FillRect(temp, 0, color);
+        SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
+        SDL_BlitSurface(temp, 0, screen, &rect);
+        SDL_FreeSurface(temp);
+      }
+    }
+  }
+
+  void
+  Renderer::draw_filled_rect(const DrawingRequest& request)
+  {
+    const FillRectRequest* fillrectrequest
+      = (FillRectRequest*) request.request_data;
+
+    SDL_Rect rect;
+    rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH;
+    rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT;
+    rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH;
+    rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT;
+    Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
+    Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
+    Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
+    Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
+    Uint32 color = SDL_MapRGB(screen->format, r, g, b);
+    if(a == SDL_ALPHA_OPAQUE) {
+      SDL_FillRect(screen, &rect, color);
+    } else if(a != SDL_ALPHA_TRANSPARENT) {
+      SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+
+      SDL_FillRect(temp, 0, color);
+      SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
+      SDL_BlitSurface(temp, 0, screen, &rect);
+      SDL_FreeSurface(temp);
+    }
+  }
+
+  void 
+  Renderer::do_take_screenshot()
+  {
+    // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
+
+    SDL_Surface *screen = SDL_GetVideoSurface();
+
+    // save screenshot
+    static const std::string writeDir = PHYSFS_getWriteDir();
+    static const std::string dirSep = PHYSFS_getDirSeparator();
+    static const std::string baseName = "screenshot";
+    static const std::string fileExt = ".bmp";
+    std::string fullFilename;
+    for (int num = 0; num < 1000; num++) {
+      std::ostringstream oss;
+      oss << baseName;
+      oss << std::setw(3) << std::setfill('0') << num;
+      oss << fileExt;
+      std::string fileName = oss.str();
+      fullFilename = writeDir + dirSep + fileName;
+      if (!PHYSFS_exists(fileName.c_str())) {
+        SDL_SaveBMP(screen, fullFilename.c_str());
+        log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
+        return;
+      }
+    }
+    log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
+  }
+
+  void
+  Renderer::flip()
+  {
+    SDL_Flip(screen);
+  }
+}
diff --git a/src/video/sdl_renderer.hpp b/src/video/sdl_renderer.hpp
new file mode 100644 (file)
index 0000000..24dcb84
--- /dev/null
@@ -0,0 +1,48 @@
+//  $Id: sdl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_SDL_RENDERER_H
+#define SUPERTUX_SDL_RENDERER_H
+
+#include <SDL_video.h>
+
+#include "renderer.hpp"
+
+namespace SDL
+{
+  class Renderer : public ::Renderer
+  {
+  public:
+    Renderer();
+    ~Renderer();
+
+    void draw_surface(const DrawingRequest& request);
+    void draw_surface_part(const DrawingRequest& request);
+    void draw_text(const DrawingRequest& request);
+    void draw_gradient(const DrawingRequest& request);
+    void draw_filled_rect(const DrawingRequest& request);
+    void do_take_screenshot();
+    void flip();
+  private:
+    SDL_Surface *screen;
+    int numerator, denominator;
+  };
+}
+
+#endif
+
diff --git a/src/video/sdl_surface_data.hpp b/src/video/sdl_surface_data.hpp
new file mode 100644 (file)
index 0000000..1e46e68
--- /dev/null
@@ -0,0 +1,80 @@
+//  $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __SDL_SURFACE_DATA_HPP__
+#define __SDL_SURFACE_DATA_HPP__
+
+#include <config.h>
+
+#include "surface.hpp"
+#include "texture.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+
+namespace SDL
+{
+  class SurfaceData
+  {
+  private:
+    const Surface &surface;
+    SDL_Rect src_rects[NUM_EFFECTS];
+
+  public:
+    SurfaceData(const Surface &surface) :
+      surface(surface)
+    {
+      int numerator, denominator;
+      float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+      float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+      if(xfactor < yfactor)
+      {
+        numerator = config->screenwidth;
+        denominator = SCREEN_WIDTH;
+      }
+      else
+      {
+        numerator = config->screenheight;
+        denominator = SCREEN_HEIGHT;
+      }
+
+      src_rects[NO_EFFECT].x = surface.get_x() * numerator / denominator;
+      src_rects[NO_EFFECT].y = surface.get_y() * numerator / denominator;
+      src_rects[NO_EFFECT].w = surface.get_width() * numerator / denominator;
+      src_rects[NO_EFFECT].h = surface.get_height() * numerator / denominator;
+
+      int flipped_x = surface.get_texture()->get_texture_width() - surface.get_x() - surface.get_width();
+      src_rects[HORIZONTAL_FLIP].x = flipped_x * numerator / denominator;
+      src_rects[HORIZONTAL_FLIP].y = surface.get_y() * numerator / denominator;
+      src_rects[HORIZONTAL_FLIP].w = surface.get_width() * numerator / denominator;
+      src_rects[HORIZONTAL_FLIP].h = surface.get_height() * numerator / denominator;
+
+      int flipped_y = surface.get_texture()->get_texture_height() - surface.get_y() - surface.get_height();
+      src_rects[VERTICAL_FLIP].x = flipped_y * numerator / denominator;
+      src_rects[VERTICAL_FLIP].y = surface.get_y() * numerator / denominator;
+      src_rects[VERTICAL_FLIP].w = surface.get_width() * numerator / denominator;
+      src_rects[VERTICAL_FLIP].h = surface.get_height() * numerator / denominator;
+    }
+
+    SDL_Rect *get_src_rect(DrawingEffect effect)
+    {
+      return src_rects + effect;
+    }
+  };
+}
+
+#endif
diff --git a/src/video/sdl_texture.cpp b/src/video/sdl_texture.cpp
new file mode 100644 (file)
index 0000000..2164550
--- /dev/null
@@ -0,0 +1,658 @@
+//  $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "sdl_texture.hpp"
+#include "color.hpp"
+#include "gameconfig.hpp"
+#include "main.hpp"
+
+#include <assert.h>
+
+#include <SDL.h>
+
+namespace
+{
+#define BILINEAR
+
+#ifdef NAIVE
+  SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
+  {
+    if(numerator == denominator)
+    {
+      src->refcount++;
+      return src;
+    }
+    else
+    {
+      SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+      int bpp = dst->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_LockSurface(src);
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_LockSurface(dst);
+      }
+      for(int y = 0;y < dst->h;y++) {
+        for(int x = 0;x < dst->w;x++) {
+          Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
+          Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+          switch(bpp) {
+            case 4:
+              dstpixel[3] = srcpixel[3];
+            case 3:
+              dstpixel[2] = srcpixel[2];
+            case 2:
+              dstpixel[1] = srcpixel[1];
+            case 1:
+              dstpixel[0] = srcpixel[0];
+          }
+        }
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_UnlockSurface(dst);
+      }
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_UnlockSurface(src);
+      }
+      if(!src->format->Amask)
+      {
+        if(src->flags & SDL_SRCALPHA)
+        {
+          SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+        }
+        if(src->flags & SDL_SRCCOLORKEY)
+        {
+          SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+        }
+      }
+      return dst;
+    }
+  }
+#endif
+
+#ifdef BILINEAR
+  void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
+  {
+    int bpp = src->format->BytesPerPixel;
+    if(srcx == src->w)
+    {
+      srcx--;
+    }
+    if(srcy == src->h)
+    {
+      srcy--;
+    }
+    Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+    Uint32 mapped = 0;
+    switch(bpp) {
+      case 1:
+        mapped = *srcpixel;
+        break;
+      case 2:
+        mapped = *(Uint16 *)srcpixel;
+        break;
+      case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+        mapped |= srcpixel[0] << 16;
+        mapped |= srcpixel[1] << 8;
+        mapped |= srcpixel[2] << 0;
+#else
+        mapped |= srcpixel[0] << 0;
+        mapped |= srcpixel[1] << 8;
+        mapped |= srcpixel[2] << 16;
+#endif
+        break;
+      case 4:
+        mapped = *(Uint32 *)srcpixel;
+        break;
+    }
+    SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
+  }
+
+  void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
+  {
+    color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
+    color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
+    color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
+    color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
+  }
+
+  SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
+  {
+    if(numerator == denominator)
+    {
+      src->refcount++;
+      return src;
+    }
+    else
+    {
+      SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+      int bpp = dst->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_LockSurface(src);
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_LockSurface(dst);
+      }
+      for(int y = 0;y < dst->h;y++) {
+        for(int x = 0;x < dst->w;x++) {
+          int srcx = x * denominator / numerator;
+          int srcy = y * denominator / numerator;
+          Uint8 color00[4], color01[4], color10[4], color11[4];
+          getpixel(src, srcx, srcy, color00);
+          getpixel(src, srcx + 1, srcy, color01);
+          getpixel(src, srcx, srcy + 1, color10);
+          getpixel(src, srcx + 1, srcy + 1, color11);
+          Uint8 color0[4], color1[4], color[4];
+          int remx = x * denominator % numerator;
+          merge(color0, color00, color01, remx, numerator);
+          merge(color1, color10, color11, remx, numerator);
+          int remy = y * denominator % numerator;
+          merge(color, color0, color1, remy, numerator);
+          Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+          Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
+          switch(bpp) {
+            case 1:
+              *dstpixel = mapped;
+              break;
+            case 2:
+              *(Uint16 *)dstpixel = mapped;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              dstpixel[0] = (mapped >> 16) & 0xff;
+              dstpixel[1] = (mapped >> 8) & 0xff;
+              dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+              dstpixel[0] = (mapped >> 0) & 0xff;
+              dstpixel[1] = (mapped >> 8) & 0xff;
+              dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+              break;
+            case 4:
+              *(Uint32 *)dstpixel = mapped;
+              break;
+          }
+        }
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_UnlockSurface(dst);
+      }
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_UnlockSurface(src);
+      }
+      if(!src->format->Amask)
+      {
+        if(src->flags & SDL_SRCALPHA)
+        {
+          SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+        }
+        if(src->flags & SDL_SRCCOLORKEY)
+        {
+          SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+        }
+      }
+      return dst;
+    }
+  }
+#endif
+
+  SDL_Surface *horz_flip(SDL_Surface *src)
+  {
+    SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+    int bpp = dst->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_LockSurface(dst);
+    }
+    for(int y = 0;y < dst->h;y++) {
+      for(int x = 0;x < dst->w;x++) {
+        Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+        Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
+        switch(bpp) {
+          case 4:
+            dstpixel[3] = srcpixel[3];
+          case 3:
+            dstpixel[2] = srcpixel[2];
+          case 2:
+            dstpixel[1] = srcpixel[1];
+          case 1:
+            dstpixel[0] = srcpixel[0];
+        }
+      }
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_UnlockSurface(dst);
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+    if(!src->format->Amask)
+    {
+      if(src->flags & SDL_SRCALPHA)
+      {
+        SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+      }
+      if(src->flags & SDL_SRCCOLORKEY)
+      {
+        SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+      }
+    }
+    return dst;
+  }
+
+  SDL_Surface *vert_flip(SDL_Surface *src)
+  {
+    SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+    int bpp = dst->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_LockSurface(dst);
+    }
+    for(int y = 0;y < dst->h;y++) {
+      for(int x = 0;x < dst->w;x++) {
+        Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+        Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
+        switch(bpp) {
+          case 4:
+            dstpixel[3] = srcpixel[3];
+          case 3:
+            dstpixel[2] = srcpixel[2];
+          case 2:
+            dstpixel[1] = srcpixel[1];
+          case 1:
+            dstpixel[0] = srcpixel[0];
+        }
+      }
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_UnlockSurface(dst);
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+    if(!src->format->Amask)
+    {
+      if(src->flags & SDL_SRCALPHA)
+      {
+        SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+      }
+      if(src->flags & SDL_SRCCOLORKEY)
+      {
+        SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+      }
+    }
+    return dst;
+  }
+
+  SDL_Surface *colorize(SDL_Surface *src, const Color &color)
+  {
+    // FIXME: This is really slow
+    assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
+    int red = (int) (color.red * 256);
+    int green = (int) (color.green * 256);
+    int blue = (int) (color.blue * 256);
+    SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
+    int bpp = dst->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_LockSurface(dst);
+    }
+    for(int y = 0;y < dst->h;y++) {
+      for(int x = 0;x < dst->w;x++) {
+        Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+        Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+        Uint32 mapped = 0;
+        switch(bpp) {
+          case 1:
+            mapped = *srcpixel;
+            break;
+          case 2:
+            mapped = *(Uint16 *)srcpixel;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            mapped |= srcpixel[0] << 16;
+            mapped |= srcpixel[1] << 8;
+            mapped |= srcpixel[2] << 0;
+#else
+            mapped |= srcpixel[0] << 0;
+            mapped |= srcpixel[1] << 8;
+            mapped |= srcpixel[2] << 16;
+#endif
+            break;
+          case 4:
+            mapped = *(Uint32 *)srcpixel;
+            break;
+        }
+        if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
+        {
+          Uint8 r, g, b, a;
+          SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
+          mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
+        }
+        switch(bpp) {
+          case 1:
+            *dstpixel = mapped;
+            break;
+          case 2:
+            *(Uint16 *)dstpixel = mapped;
+            break;
+          case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+            dstpixel[0] = (mapped >> 16) & 0xff;
+            dstpixel[1] = (mapped >> 8) & 0xff;
+            dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+            dstpixel[0] = (mapped >> 0) & 0xff;
+            dstpixel[1] = (mapped >> 8) & 0xff;
+            dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+            break;
+          case 4:
+            *(Uint32 *)dstpixel = mapped;
+            break;
+        }
+      }
+    }
+    if(SDL_MUSTLOCK(dst))
+    {
+      SDL_UnlockSurface(dst);
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+    if(!src->format->Amask)
+    {
+      if(src->flags & SDL_SRCALPHA)
+      {
+        SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+      }
+      if(src->flags & SDL_SRCCOLORKEY)
+      {
+        SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+      }
+    }
+    return dst;
+  }
+
+  SDL_Surface *optimize(SDL_Surface *src)
+  {
+    if(!src->format->Amask)
+    {
+      return SDL_DisplayFormat(src);
+    }
+    else
+    {
+      int transparent = 0;
+      int opaque = 0;
+      int semitransparent = 0;
+      int alphasum = 0;
+      int squaredalphasum = 0;
+      bool colors[(1 << 12)];
+      memset(colors, 0, (1 << 12) * sizeof(bool));
+
+      int bpp = src->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_LockSurface(src);
+      }
+      for(int y = 0;y < src->h;y++) {
+        for(int x = 0;x < src->w;x++) {
+          Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+          Uint32 mapped = 0;
+          switch(bpp) {
+            case 1:
+              mapped = *pixel;
+              break;
+            case 2:
+              mapped = *(Uint16 *)pixel;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              mapped |= pixel[0] << 16;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 0;
+#else
+              mapped |= pixel[0] << 0;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 16;
+#endif
+              break;
+            case 4:
+              mapped = *(Uint32 *)pixel;
+              break;
+          }
+          Uint8 red, green, blue, alpha;
+          SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+          if(alpha < 16)
+          {
+            transparent++;
+          }
+          else if (alpha > 240)
+          {
+            opaque++;
+            alphasum += alpha;
+            squaredalphasum += alpha * alpha;
+          }
+          else
+          {
+            semitransparent++;
+            squaredalphasum += alpha * alpha;
+          }
+          if(alpha != 0)
+          {
+            colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
+          }
+        }
+      }
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_UnlockSurface(src);
+      }
+      int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
+      int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
+      int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
+      if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
+      {
+        return SDL_DisplayFormatAlpha(src);
+      }
+      int keycolor = -1;
+      for(int i = 0;i < (1 << 12);i++)
+      {
+        if(!colors[i])
+        {
+          keycolor = i;
+        }
+      }
+      if(keycolor == -1)
+      {
+        return SDL_DisplayFormatAlpha(src);
+      }
+      SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA), src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, 0);
+      bpp = dst->format->BytesPerPixel;
+      Uint32 key = SDL_MapRGB(dst->format, (((keycolor & 0xf00) >> 4) | 0xf), ((keycolor & 0xf0) | 0xf), (((keycolor & 0xf) << 4) | 0xf));
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_LockSurface(src);
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_LockSurface(dst);
+      }
+      for(int y = 0;y < dst->h;y++) {
+        for(int x = 0;x < dst->w;x++) {
+          Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+          Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+          Uint32 mapped = 0;
+          switch(bpp) {
+            case 1:
+              mapped = *srcpixel;
+              break;
+            case 2:
+              mapped = *(Uint16 *)srcpixel;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              mapped |= srcpixel[0] << 16;
+              mapped |= srcpixel[1] << 8;
+              mapped |= srcpixel[2] << 0;
+#else
+              mapped |= srcpixel[0] << 0;
+              mapped |= srcpixel[1] << 8;
+              mapped |= srcpixel[2] << 16;
+#endif
+              break;
+            case 4:
+              mapped = *(Uint32 *)srcpixel;
+              break;
+          }
+          Uint8 red, green, blue, alpha;
+          SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+          if(alpha < (avgalpha / 4))
+          {
+            mapped = key;
+          }
+          else
+          {
+            mapped = SDL_MapRGB(dst->format, red, green, blue);
+          }
+          switch(bpp) {
+            case 1:
+              *dstpixel = mapped;
+              break;
+            case 2:
+              *(Uint16 *)dstpixel = mapped;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              dstpixel[0] = (mapped >> 16) & 0xff;
+              dstpixel[1] = (mapped >> 8) & 0xff;
+              dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+              dstpixel[0] = (mapped >> 0) & 0xff;
+              dstpixel[1] = (mapped >> 8) & 0xff;
+              dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+              break;
+            case 4:
+              *(Uint32 *)dstpixel = mapped;
+              break;
+          }
+        }
+      }
+      if(SDL_MUSTLOCK(dst))
+      {
+        SDL_UnlockSurface(dst);
+      }
+      if(SDL_MUSTLOCK(src))
+      {
+        SDL_UnlockSurface(src);
+      }
+      if(avgalpha < 240)
+      {
+        SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
+      }
+      SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
+      SDL_Surface *convert = SDL_DisplayFormat(dst);
+      SDL_FreeSurface(dst);
+      return convert;
+    }
+  }
+}
+
+namespace SDL
+{
+  Texture::Texture(SDL_Surface* image)
+  {
+    texture = optimize(image);
+    //width = texture->w;
+    //height = texture->h;
+    int numerator, denominator;
+    float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+    float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+    if(xfactor < yfactor)
+    {
+      numerator = config->screenwidth;
+      denominator = SCREEN_WIDTH;
+    }
+    else
+    {
+      numerator = config->screenheight;
+      denominator = SCREEN_HEIGHT;
+    }
+    cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
+  }
+
+  Texture::~Texture()
+  {
+    SDL_FreeSurface(texture);
+  }
+
+  SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
+  {
+    if(cache[NO_EFFECT][color] == 0) {
+      assert(cache[NO_EFFECT][Color::WHITE]);
+      cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
+    }
+    if(cache[effect][color] == 0) {
+      assert(cache[NO_EFFECT][color]);
+      switch(effect) {
+        case NO_EFFECT:
+          break;
+        case HORIZONTAL_FLIP:
+          cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
+          break;
+        case VERTICAL_FLIP:
+          cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
+          break;
+        default:
+          return 0;
+      }
+    }
+    return cache[effect][color];
+  }
+}
diff --git a/src/video/sdl_texture.hpp b/src/video/sdl_texture.hpp
new file mode 100644 (file)
index 0000000..34479db
--- /dev/null
@@ -0,0 +1,140 @@
+//  $Id: sdl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __SDL_TEXTURE_HPP__
+#define __SDL_TEXTURE_HPP__
+
+#include <config.h>
+
+#include <SDL.h>
+
+#include "texture.hpp"
+#include "color.hpp"
+
+namespace SDL
+{
+  class Texture : public ::Texture
+  {
+  protected:
+    SDL_Surface *texture;
+    //unsigned int width;
+    //unsigned int height;
+
+    struct ColorCache
+    {
+      static const int HASHED_BITS = 3;
+      static const int CACHE_SIZE = 1 << (HASHED_BITS * 3);
+
+      static void ref(SDL_Surface *surface)
+      {
+        if(surface)
+        {
+          surface->refcount++;
+        }
+      }
+
+      static int hash(const Color &color)
+      {
+        return
+      ((int) (color.red * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1) * 2) |
+      ((int) (color.green * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1)) |
+      ((int) (color.blue * ((1 << HASHED_BITS) - 1)) << 0);
+      }
+
+      SDL_Surface *data[CACHE_SIZE];
+
+      ColorCache()
+      {
+        memset(data, 0, CACHE_SIZE * sizeof(SDL_Surface *));
+      }
+
+      ~ColorCache()
+      {
+        std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
+      }
+
+      void operator = (const ColorCache &other)
+      {
+        std::for_each(other.data, other.data + CACHE_SIZE, ref);
+        std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
+        memcpy(data, other.data, CACHE_SIZE * sizeof(SDL_Surface *));
+      }
+
+      SDL_Surface *&operator [] (const Color &color)
+      {
+        return data[hash(color)];
+      }
+    };
+    //typedef std::map<Color, SDL_Surface *> ColorCache;
+    ColorCache cache[NUM_EFFECTS];
+
+  public:
+    Texture(SDL_Surface* sdlsurface);
+    virtual ~Texture();
+
+    SDL_Surface *get_transform(const Color &color, DrawingEffect effect);
+
+    SDL_Surface *get_texture() const
+    {
+      return texture;
+    }
+
+    unsigned int get_texture_width() const
+    {
+      return texture->w;
+    }
+
+    unsigned int get_texture_height() const
+    {
+      return texture->h;
+    }
+
+    unsigned int get_image_width() const
+    {
+      return texture->w;
+    }
+
+    unsigned int get_image_height() const
+    {
+      return texture->h;
+    }
+
+    /*unsigned int get_texture_width() const
+    {
+      return width;
+    }
+
+    unsigned int get_texture_height() const
+    {
+      return height;
+    }
+
+    unsigned int get_image_width() const
+    {
+      return width;
+    }
+
+    unsigned int get_image_height() const
+    {
+      return height;
+    }*/
+  };
+}
+
+#endif
diff --git a/src/video/surface.hpp b/src/video/surface.hpp
new file mode 100644 (file)
index 0000000..d90494b
--- /dev/null
@@ -0,0 +1,147 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __SURFACE_HPP__
+#define __SURFACE_HPP__
+
+#include <config.h>
+
+#include <string>
+#include <SDL.h>
+#include "math/vector.hpp"
+#include "texture.hpp"
+#include "video_systems.hpp"
+
+/**
+ * A rectangular image.
+ * The class basically holds a reference to a texture with additional UV
+ * coordinates that specify a rectangular area on this texture
+ */
+class Surface
+{
+private:
+  Texture* texture;
+  void *surface_data;
+  int x;
+  int y;
+  int w;
+  int h;
+  bool flipx;
+
+public:
+  Surface(const std::string& file) :
+    texture(texture_manager->get(file)),
+    x(0), y(0), w(0), h(0),
+    flipx(false)
+  {
+    texture->ref();
+    w = texture->get_image_width();
+    h = texture->get_image_height();
+    surface_data = new_surface_data(*this);
+  }
+
+  Surface(const std::string& file, int x, int y, int w, int h) :
+    texture(texture_manager->get(file)),
+    x(x), y(y), w(w), h(h),
+    flipx(false)
+  {
+    texture->ref();
+    surface_data = new_surface_data(*this);
+  }
+
+  Surface(const Surface& other) :
+    texture(other.texture),
+    x(other.x), y(other.y),
+    w(other.w), h(other.h),
+    flipx(false)
+  {
+    texture->ref();
+    surface_data = new_surface_data(*this);
+  }
+
+  ~Surface()
+  {
+    free_surface_data(surface_data);
+    texture->unref();
+  }
+
+  /** flip the surface horizontally */
+  void hflip()
+  {
+    flipx = !flipx;
+  }
+
+  bool get_flipx() const
+  {
+    return flipx;
+  }
+
+  const Surface& operator= (const Surface& other)
+  {
+    other.texture->ref();
+    texture->unref();
+    texture = other.texture;
+    x = other.x;
+    y = other.y;
+    w = other.w;
+    h = other.h;
+    return *this;
+  }
+
+  Texture *get_texture() const
+  {
+    return texture;
+  }
+
+  void *get_surface_data() const
+  {
+    return surface_data;
+  }
+
+  int get_x() const
+  {
+    return x;
+  }
+
+  int get_y() const
+  {
+    return y;
+  }
+
+  int get_width() const
+  {
+    return w;
+  }
+
+  int get_height() const
+  {
+    return h;
+  }
+
+  Vector get_position() const
+  { return Vector(get_x(), get_y()); }
+
+  /**
+   * returns a vector containing width and height
+   */
+  Vector get_size() const
+  { return Vector(get_width(), get_height()); }
+};
+
+#endif
diff --git a/src/video/texture.hpp b/src/video/texture.hpp
new file mode 100644 (file)
index 0000000..6e4c462
--- /dev/null
@@ -0,0 +1,91 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __TEXTURE_HPP__
+#define __TEXTURE_HPP__
+
+#include <config.h>
+
+#include <assert.h>
+#include <string>
+
+#include "texture_manager.hpp"
+
+/// bitset for drawing effects
+enum DrawingEffect {
+  /** Don't apply anything */
+  NO_EFFECT,
+  /** Draw the Surface upside down */
+  VERTICAL_FLIP,
+  /** Draw the Surface from left to down */
+  HORIZONTAL_FLIP,
+  NUM_EFFECTS
+};
+
+/**
+ * This class is a wrapper around a texture handle. It stores the texture width
+ * and height and provides convenience functions for uploading SDL_Surfaces
+ * into the texture
+ */
+class Texture
+{
+protected:
+  int refcount;
+  std::string filename;
+
+public:
+  Texture() : refcount(0), filename() {}
+  virtual ~Texture() {}
+
+  virtual unsigned int get_texture_width() const = 0;
+  virtual unsigned int get_texture_height() const = 0;
+  virtual unsigned int get_image_width() const = 0;
+  virtual unsigned int get_image_height() const = 0;
+
+  std::string get_filename() const
+  {
+    return filename;
+  }
+
+  void set_filename(std::string filename)
+  {
+    this->filename = filename;
+  }
+
+  void ref()
+  {
+    refcount++;
+  }
+
+  void unref()
+  {
+    assert(refcount > 0);
+    refcount--;
+    if(refcount == 0)
+      release();
+  }
+
+private:
+  void release()
+  {
+    texture_manager->release(this);
+  }
+};
+
+#endif
diff --git a/src/video/texture_manager.cpp b/src/video/texture_manager.cpp
new file mode 100644 (file)
index 0000000..d9ff2a7
--- /dev/null
@@ -0,0 +1,214 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include "texture_manager.hpp"
+
+#include <assert.h>
+#include <SDL.h>
+#include <SDL_image.h>
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include "physfs/physfs_sdl.hpp"
+#include "video_systems.hpp"
+#include "gl_texture.hpp"
+#include "glutil.hpp"
+#include "gameconfig.hpp"
+#include "file_system.hpp"
+#include "log.hpp"
+#include "texture.hpp"
+
+TextureManager* texture_manager = NULL;
+
+TextureManager::TextureManager()
+{
+}
+
+TextureManager::~TextureManager()
+{
+  for(ImageTextures::iterator i = image_textures.begin();
+      i != image_textures.end(); ++i) {
+    if(i->second == NULL)
+      continue;
+    log_warning << "Texture '" << i->first << "' not freed" << std::endl;
+    delete i->second;
+  }
+}
+
+Texture*
+TextureManager::get(const std::string& _filename)
+{
+  std::string filename = FileSystem::normalize(_filename);
+  ImageTextures::iterator i = image_textures.find(filename);
+
+  Texture* texture = NULL;
+  if(i != image_textures.end())
+    texture = i->second;
+
+  if(texture == NULL) {
+    texture = create_image_texture(filename);
+    image_textures[filename] = texture;
+  }
+
+  return texture;
+}
+
+void
+TextureManager::release(Texture* texture)
+{
+  image_textures.erase(texture->get_filename());
+  delete texture;
+}
+
+#ifdef HAVE_OPENGL
+void
+TextureManager::register_texture(GL::Texture* texture)
+{
+  textures.insert(texture);
+}
+
+void
+TextureManager::remove_texture(GL::Texture* texture)
+{
+  textures.erase(texture);
+}
+#endif
+
+Texture*
+TextureManager::create_image_texture(const std::string& filename)
+{
+  SDL_Surface* image = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
+  if(image == 0) {
+    std::ostringstream msg;
+    msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
+    throw std::runtime_error(msg.str());
+  }
+
+  Texture* result = 0;
+  try {
+    result = new_texture(image);
+    result->set_filename(filename);
+  } catch(...) {
+    delete result;
+    SDL_FreeSurface(image);
+    throw;
+  }
+
+  SDL_FreeSurface(image);
+  return result;
+}
+
+#ifdef HAVE_OPENGL
+void
+TextureManager::save_textures()
+{
+  glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+  glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
+  glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+  glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+  glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
+  glPixelStorei(GL_PACK_ALIGNMENT, 1);
+  for(Textures::iterator i = textures.begin(); i != textures.end(); ++i) {
+    save_texture(*i);
+  }
+  for(ImageTextures::iterator i = image_textures.begin();
+      i != image_textures.end(); ++i) {
+    save_texture(dynamic_cast<GL::Texture *>(i->second));
+  }
+}
+
+void
+TextureManager::save_texture(GL::Texture* texture)
+{
+  SavedTexture saved_texture;
+  saved_texture.texture = texture;
+  glBindTexture(GL_TEXTURE_2D, texture->get_handle());
+  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
+                           &saved_texture.width);
+  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
+                           &saved_texture.height);
+  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER,
+                           &saved_texture.border);
+  glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                      &saved_texture.min_filter);
+  glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+                      &saved_texture.mag_filter);
+  glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                      &saved_texture.wrap_s);
+  glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                      &saved_texture.wrap_t);
+
+  size_t pixelssize = saved_texture.width * saved_texture.height * 4;
+  saved_texture.pixels = new char[pixelssize];
+
+  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                saved_texture.pixels);
+
+  saved_textures.push_back(saved_texture);
+
+  glDeleteTextures(1, &(texture->get_handle()));
+  texture->set_handle(0);
+
+  assert_gl("retrieving texture for save");
+}
+
+void
+TextureManager::reload_textures()
+{
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+  glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
+  glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+  glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+  glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+  for(std::vector<SavedTexture>::iterator i = saved_textures.begin();
+      i != saved_textures.end(); ++i) {
+    SavedTexture& saved_texture = *i;
+
+    GLuint handle;
+    glGenTextures(1, &handle);
+    assert_gl("creating texture handle");
+
+    glBindTexture(GL_TEXTURE_2D, handle);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                 saved_texture.width, saved_texture.height,
+                 saved_texture.border, GL_RGBA,
+                 GL_UNSIGNED_BYTE, saved_texture.pixels);
+    delete[] saved_texture.pixels;
+    assert_gl("uploading texture pixel data");
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                    saved_texture.min_filter);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+                    saved_texture.mag_filter);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                    saved_texture.wrap_s);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                    saved_texture.wrap_t);
+
+    assert_gl("setting texture_params");
+    saved_texture.texture->set_handle(handle);
+  }
+
+  saved_textures.clear();
+}
+#endif
diff --git a/src/video/texture_manager.hpp b/src/video/texture_manager.hpp
new file mode 100644 (file)
index 0000000..1431c68
--- /dev/null
@@ -0,0 +1,84 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef __IMAGE_TEXTURE_MANAGER_HPP__
+#define __IMAGE_TEXTURE_MANAGER_HPP__
+
+#include <config.h>
+
+#include "glutil.hpp"
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+class Texture;
+namespace GL { class Texture; }
+
+class TextureManager
+{
+public:
+  TextureManager();
+  ~TextureManager();
+
+  Texture* get(const std::string& filename);
+
+#ifdef HAVE_OPENGL
+  void register_texture(GL::Texture* texture);
+  void remove_texture(GL::Texture* texture);
+
+  void save_textures();
+  void reload_textures();
+#endif
+
+private:
+  friend class Texture;
+  void release(Texture* texture);
+
+  typedef std::map<std::string, Texture*> ImageTextures;
+  ImageTextures image_textures;
+
+  Texture* create_image_texture(const std::string& filename);
+
+#ifdef HAVE_OPENGL
+  typedef std::set<GL::Texture*> Textures;
+  Textures textures;
+
+  struct SavedTexture
+  {
+    GL::Texture* texture;
+    GLint width;
+    GLint height;
+    char* pixels;
+    GLint border;
+
+    GLint min_filter;
+    GLint mag_filter;
+    GLint wrap_s;
+    GLint wrap_t;
+  };
+  std::vector<SavedTexture> saved_textures;
+
+  void save_texture(GL::Texture* texture);
+#endif
+};
+
+extern TextureManager* texture_manager;
+
+#endif
diff --git a/src/video/video_systems.cpp b/src/video/video_systems.cpp
new file mode 100644 (file)
index 0000000..1fd3627
--- /dev/null
@@ -0,0 +1,180 @@
+//  $Id: video_systems.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "video_systems.hpp"
+#include "gameconfig.hpp"
+#include "renderer.hpp"
+#include "gl_renderer.hpp"
+#include "sdl_renderer.hpp"
+#include "lightmap.hpp"
+#include "gl_lightmap.hpp"
+#include "sdl_lightmap.hpp"
+#include "texture.hpp"
+#include "gl_texture.hpp"
+#include "sdl_texture.hpp"
+#include "gl_surface_data.hpp"
+#include "sdl_surface_data.hpp"
+
+Renderer *new_renderer()
+{
+  switch(config->video)
+  {
+    case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+      return new GL::Renderer();
+#else
+      return new SDL::Renderer();
+#endif
+#ifdef HAVE_OPENGL
+    case OPENGL:
+      return new GL::Renderer();
+#endif
+    case PURE_SDL:
+      return new SDL::Renderer();
+    default:
+      assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+      return new GL::Renderer();
+#else
+      return new SDL::Renderer();
+#endif
+  }
+}
+
+Lightmap *new_lightmap()
+{
+  switch(config->video)
+  {
+    case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+      return new GL::Lightmap();
+#else
+      return new SDL::Lightmap();
+#endif
+#ifdef HAVE_OPENGL
+    case OPENGL:
+      return new GL::Lightmap();
+#endif
+    case PURE_SDL:
+      return new SDL::Lightmap();
+    default:
+      assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+      return new GL::Lightmap();
+#else
+      return new SDL::Lightmap();
+#endif
+  }
+}
+
+Texture *new_texture(SDL_Surface *image)
+{
+  switch(config->video)
+  {
+    case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+      return new GL::Texture(image);
+#else
+      return new SDL::Texture(image);
+#endif
+#ifdef HAVE_OPENGL
+    case OPENGL:
+      return new GL::Texture(image);
+#endif
+    case PURE_SDL:
+      return new SDL::Texture(image);
+    default:
+      assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+      return new GL::Texture(image);
+#else
+      return new SDL::Texture(image);
+#endif
+  }
+}
+
+void *new_surface_data(const Surface &surface)
+{
+  switch(config->video)
+  {
+    case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+      return new GL::SurfaceData(surface);
+#else
+      return new SDL::SurfaceData(surface);
+#endif
+#ifdef HAVE_OPENGL
+    case OPENGL:
+      return new GL::SurfaceData(surface);
+#endif
+    case PURE_SDL:
+      return new SDL::SurfaceData(surface);
+    default:
+      assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+      return new GL::SurfaceData(surface);
+#else
+      return new SDL::SurfaceData(surface);
+#endif
+  }
+}
+
+void free_surface_data(void *surface_data)
+{
+  delete reinterpret_cast<char *>(surface_data);
+}
+
+VideoSystem get_video_system(const std::string &video)
+{
+  if(video == "auto")
+  {
+    return AUTO_VIDEO;
+  }
+#ifdef HAVE_OPENGL
+  else if(video == "opengl")
+  {
+    return OPENGL;
+  }
+#endif
+  else if(video == "sdl")
+  {
+    return PURE_SDL;
+  }
+  else
+  {
+    return AUTO_VIDEO;
+  }
+}
+
+std::string get_video_string(VideoSystem video)
+{
+  switch(video)
+  {
+    case AUTO_VIDEO:
+      return "auto";
+    case OPENGL:
+      return "opengl";
+    case PURE_SDL:
+      return "sdl";
+    default:
+      assert(0 && "invalid video system in config");
+      return "auto";
+  }
+}
diff --git a/src/video/video_systems.hpp b/src/video/video_systems.hpp
new file mode 100644 (file)
index 0000000..1347f54
--- /dev/null
@@ -0,0 +1,48 @@
+//  $Id: video_systems.hpp 5138 2007-08-15 01:02:22Z tuxdev $
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#ifndef __RENDER_SYTSTEMS_HPP__
+#define __RENDER_SYTSTEMS_HPP__
+
+#include <config.h>
+
+#include <string>
+#include <SDL.h>
+
+class Renderer;
+class Lightmap;
+class Texture;
+class Surface;
+
+enum VideoSystem {
+  AUTO_VIDEO,
+  OPENGL,
+  PURE_SDL,
+  NUM_SYSTEMS
+};
+
+Renderer *new_renderer();
+Lightmap *new_lightmap();
+Texture *new_texture(SDL_Surface *image);
+void *new_surface_data(const Surface &surface);
+void free_surface_data(void *surface_data);
+VideoSystem get_video_system(const std::string &video);
+std::string get_video_string(VideoSystem video);
+
+#endif
diff --git a/src/world.cpp b/src/world.cpp
new file mode 100644 (file)
index 0000000..8fd4599
--- /dev/null
@@ -0,0 +1,253 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+#include <config.h>
+
+#include <stddef.h>
+#include <physfs.h>
+#include <stdexcept>
+
+#include "world.hpp"
+#include "file_system.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "scripting/serialize.hpp"
+#include "log.hpp"
+#include "worldmap/worldmap.hpp"
+#include "mainloop.hpp"
+
+static bool has_suffix(const std::string& data, const std::string& suffix)
+{
+  if (data.length() >= suffix.length())
+    return data.compare(data.length() - suffix.length(), suffix.length(), suffix) == 0;
+  else
+    return false;
+}
+
+World* World::current_ = NULL;
+
+World::World()
+{
+  is_levelset = true;
+  hide_from_contribs = false;
+  sq_resetobject(&world_thread);
+}
+
+World::~World()
+{
+  sq_release(Scripting::global_vm, &world_thread);
+  if(current_ == this)
+    current_ = NULL;
+}
+
+void
+World::set_savegame_filename(const std::string& filename)
+{
+  this->savegame_filename = filename;
+  // make sure the savegame directory exists
+  std::string dirname = FileSystem::dirname(filename);
+  if(!PHYSFS_exists(dirname.c_str())) {
+      if(PHYSFS_mkdir(dirname.c_str())) {
+          std::ostringstream msg;
+          msg << "Couldn't create directory for savegames '"
+              << dirname << "': " <<PHYSFS_getLastError();
+          throw std::runtime_error(msg.str());
+      }
+  }
+
+  if(!PHYSFS_isDirectory(dirname.c_str())) {
+      std::ostringstream msg;
+      msg << "Savegame path '" << dirname << "' is not a directory";
+      throw std::runtime_error(msg.str());
+  }
+}
+
+void
+World::load(const std::string& filename)
+{
+  basedir = FileSystem::dirname(filename);
+
+  lisp::Parser parser;
+  const lisp::Lisp* root = parser.parse(filename);
+
+  const lisp::Lisp* info = root->get_lisp("supertux-world");
+  if(info == NULL)
+    info = root->get_lisp("supertux-level-subset");
+  if(info == NULL)
+    throw std::runtime_error("File is not a world or levelsubset file");
+
+  hide_from_contribs = false;
+  is_levelset = true;
+
+  info->get("title", title);
+  info->get("description", description);
+  info->get("levelset", is_levelset);
+  info->get_vector("levels", levels);
+  info->get("hide-from-contribs", hide_from_contribs);
+
+  // Level info file doesn't define any levels, so read the
+  // directory to see what we can find
+
+  std::string path = basedir;
+  char** files = PHYSFS_enumerateFiles(path.c_str());
+  if(!files) {
+    log_warning << "Couldn't read subset dir '" << path << "'" << std::endl;
+    return;
+  }
+
+  for(const char* const* filename = files; *filename != 0; ++filename) {
+    if(has_suffix(*filename, ".stl")) {
+      levels.push_back(path + *filename);
+    }
+  }
+  PHYSFS_freeList(files);
+}
+
+void
+World::run()
+{
+  using namespace Scripting;
+
+  current_ = this;
+
+  // create new squirrel table for persisten game state
+  HSQUIRRELVM vm = Scripting::global_vm;
+
+  sq_pushroottable(vm);
+  sq_pushstring(vm, "state", -1);
+  sq_newtable(vm);
+  if(SQ_FAILED(sq_createslot(vm, -3)))
+    throw Scripting::SquirrelError(vm, "Couldn't create state table");
+  sq_pop(vm, 1);
+
+  load_state();
+
+  std::string filename = basedir + "/world.nut";
+  try {
+    IFileStream in(filename);
+
+    sq_release(global_vm, &world_thread);
+    world_thread = create_thread(global_vm);
+    compile_and_run(object_to_vm(world_thread), in, filename);
+  } catch(std::exception& ) {
+    // fallback: try to load worldmap worldmap.stwm
+    using namespace WorldMapNS;
+    main_loop->push_screen(new WorldMap(basedir + "worldmap.stwm"));
+  }
+}
+
+void
+World::save_state()
+{
+  using namespace Scripting;
+
+  lisp::Writer writer(savegame_filename);
+
+  writer.start_list("supertux-savegame");
+  writer.write_int("version", 1);
+
+  using namespace WorldMapNS;
+  if(WorldMap::current() != NULL) {
+    std::ostringstream title;
+    title << WorldMap::current()->get_title();
+    title << " (" << WorldMap::current()->solved_level_count()
+          << "/" << WorldMap::current()->level_count() << ")";
+    writer.write_string("title", title.str());
+  }
+
+  writer.start_list("tux");
+  player_status->write(writer);
+  writer.end_list("tux");
+
+  writer.start_list("state");
+
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "state", -1);
+  if(SQ_SUCCEEDED(sq_get(global_vm, -2))) {
+    Scripting::save_squirrel_table(global_vm, -1, writer);
+    sq_pop(global_vm, 1);
+  }
+  sq_pop(global_vm, 1);
+  writer.end_list("state");
+
+  writer.end_list("supertux-savegame");
+}
+
+void
+World::load_state()
+{
+  using namespace Scripting;
+
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(savegame_filename);
+
+    const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
+    if(lisp == NULL)
+      throw std::runtime_error("file is not a supertux-savegame file");
+
+    int version = 1;
+    lisp->get("version", version);
+    if(version != 1)
+      throw std::runtime_error("incompatible savegame version");
+
+    const lisp::Lisp* tux = lisp->get_lisp("tux");
+    if(tux == NULL)
+      throw std::runtime_error("No tux section in savegame");
+    player_status->read(*tux);
+
+    const lisp::Lisp* state = lisp->get_lisp("state");
+    if(state == NULL)
+      throw std::runtime_error("No state section in savegame");
+
+    sq_pushroottable(global_vm);
+    sq_pushstring(global_vm, "state", -1);
+    if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+      sq_pop(global_vm, 1);
+
+    sq_pushstring(global_vm, "state", -1);
+    sq_newtable(global_vm);
+    load_squirrel_table(global_vm, -1, state);
+    if(SQ_FAILED(sq_createslot(global_vm, -3)))
+      throw std::runtime_error("Couldn't create state table");
+    sq_pop(global_vm, 1);
+  } catch(std::exception& e) {
+    log_debug << "Couldn't load savegame: " << e.what() << std::endl;
+  }
+}
+
+const std::string&
+World::get_level_filename(unsigned int i) const
+{
+  return levels[i];
+}
+
+unsigned int
+World::get_num_levels() const
+{
+  return levels.size();
+}
+
+const std::string&
+World::get_basedir() const
+{
+  return basedir;
+}
diff --git a/src/world.hpp b/src/world.hpp
new file mode 100644 (file)
index 0000000..f59e5cc
--- /dev/null
@@ -0,0 +1,65 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_WORLD_H
+#define SUPERTUX_WORLD_H
+
+#include <vector>
+#include <string>
+#include <squirrel.h>
+
+class World
+{
+private:
+  std::vector<std::string> levels;
+  std::string basedir;
+  std::string savegame_filename;
+  /// squirrel table that saves persistent state (about the world)
+  HSQOBJECT state_table;
+  HSQOBJECT world_thread;
+  static World* current_;
+
+public:
+  World();
+  ~World();
+
+  void set_savegame_filename(const std::string& filename);
+  void load(const std::string& filename);
+
+  void save_state();
+  void load_state();
+
+  const std::string& get_level_filename(unsigned int i) const;
+  unsigned int get_num_levels() const;
+
+  const std::string& get_basedir() const;
+
+  static World* current()
+  {
+    return current_;
+  }
+
+  void run();
+
+  std::string title;
+  std::string description;
+  bool hide_from_contribs;
+  bool is_levelset;
+};
+
+#endif
diff --git a/src/worldmap/direction.hpp b/src/worldmap/direction.hpp
new file mode 100644 (file)
index 0000000..9e1b223
--- /dev/null
@@ -0,0 +1,28 @@
+//  $Id$
+//
+//  SuperTux - Worldmap Direction
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_DIRECTION_HPP__
+#define __WORLDMAP_DIRECTION_HPP__
+
+namespace WorldMapNS {
+
+enum Direction { D_NONE, D_WEST, D_EAST, D_NORTH, D_SOUTH };
+
+}
+
+#endif
diff --git a/src/worldmap/level.cpp b/src/worldmap/level.cpp
new file mode 100644 (file)
index 0000000..2b61124
--- /dev/null
@@ -0,0 +1,86 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <stddef.h>
+#include <physfs.h>
+#include "worldmap/level.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "file_system.hpp"
+
+namespace WorldMapNS
+{
+
+LevelTile::LevelTile(const std::string& basedir, const lisp::Lisp* lisp)
+  : solved(false), auto_play(false), basedir(basedir), picture_cached(false),
+    picture(0)
+{
+  lisp->get("name", name);
+  lisp->get("x", pos.x);
+  lisp->get("y", pos.y);
+  lisp->get("auto-play", auto_play);
+
+  std::string spritefile = "images/worldmap/common/leveldot.sprite";
+  lisp->get("sprite", spritefile);
+  sprite.reset(sprite_manager->create(spritefile));
+
+  lisp->get("extro-script", extro_script);
+
+  if (!PHYSFS_exists((basedir + name).c_str()))
+  {
+    log_warning << "level file '" << name
+      << "' does not exist and will not be added to the worldmap" << std::endl;
+    return;
+  }
+}
+
+LevelTile::~LevelTile()
+{
+  delete picture;
+}
+
+void
+LevelTile::draw(DrawingContext& context)
+{
+  sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+LevelTile::update(float )
+{
+}
+
+const Surface*
+LevelTile::get_picture()
+{
+  if (picture_cached) return picture;
+  picture_cached = true;
+  std::string fname = FileSystem::strip_extension(basedir + name)+".jpg";
+  if (!PHYSFS_exists(fname.c_str())) {
+       return 0;
+  }
+  picture = new Surface(fname);
+  return picture;
+}
+
+}
diff --git a/src/worldmap/level.hpp b/src/worldmap/level.hpp
new file mode 100644 (file)
index 0000000..67945e3
--- /dev/null
@@ -0,0 +1,69 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __LEVEL_TILE_HPP__
+#define __LEVEL_TILE_HPP__
+
+#include <memory>
+#include <string>
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "statistics.hpp"
+#include "video/surface.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class LevelTile : public GameObject
+{
+public:
+  LevelTile(const std::string& basedir, const lisp::Lisp* lisp);
+  virtual ~LevelTile();
+
+  virtual void draw(DrawingContext& context);
+  virtual void update(float elapsed_time);
+
+  Vector pos;
+  std::string title;
+  bool solved;
+  bool auto_play; /**< true if Tux should automatically enter this level if it's unfinished */
+
+  std::auto_ptr<Sprite> sprite;
+
+  /** Statistics for level tiles */
+  Statistics statistics;
+
+  /** Script that is run when the level is successfully finished */
+  std::string extro_script;
+
+  /** return Surface of level picture or 0 if no picture is available */
+  const Surface* get_picture();
+
+private:
+  std::string basedir;
+  bool picture_cached;
+  Surface* picture;
+
+};
+
+}
+
+#endif
diff --git a/src/worldmap/spawn_point.cpp b/src/worldmap/spawn_point.cpp
new file mode 100644 (file)
index 0000000..a74921d
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  SuperTux - Worldmap Spawnpoint
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <config.h>
+
+#include <stdexcept>
+#include <iostream>
+#include "spawn_point.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+namespace WorldMapNS
+{
+
+// from worldmap.cpp
+Direction string_to_direction(const std::string& directory);
+
+SpawnPoint::SpawnPoint(const lisp::Lisp* slisp) : auto_dir(D_NONE)
+{
+    pos.x = -1;
+    pos.y = -1;
+    lisp::ListIterator iter(slisp);
+    while(iter.next()) {
+        const std::string& token = iter.item();
+        if(token == "name") {
+            iter.value()->get(name);
+        } else if(token == "x") {
+            iter.value()->get(pos.x);
+        } else if(token == "y") {
+            iter.value()->get(pos.y);
+        } else if(token == "auto-dir") {
+            std::string s = "";
+            iter.value()->get(s);
+            auto_dir = string_to_direction(s);
+        } else {
+            log_warning << "unknown token '" << token << "' in SpawnPoint" << std::endl;
+        }
+    }
+
+    if(name == "")
+        throw std::runtime_error("No name specified for spawnpoint");
+    if(pos.x < 0 || pos.y < 0)
+        throw std::runtime_error("Invalid coordinates for spawnpoint");
+}
+
+}
diff --git a/src/worldmap/spawn_point.hpp b/src/worldmap/spawn_point.hpp
new file mode 100644 (file)
index 0000000..9d0d625
--- /dev/null
@@ -0,0 +1,43 @@
+//  $Id$
+//
+//  SuperTux - Worldmap Spawnpoint
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_SPAWN_POINT_H__
+#define __WORLDMAP_SPAWN_POINT_H__
+
+#include <string>
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "game_object.hpp"
+#include "worldmap/direction.hpp"
+
+namespace WorldMapNS
+{
+
+class SpawnPoint
+{
+public:
+    SpawnPoint(const lisp::Lisp* lisp);
+
+    std::string name;
+    Vector pos;
+    Direction auto_dir; /**< automatically start walking in this direction */
+};
+
+}
+
+#endif
diff --git a/src/worldmap/special_tile.cpp b/src/worldmap/special_tile.cpp
new file mode 100644 (file)
index 0000000..a8e5616
--- /dev/null
@@ -0,0 +1,85 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "worldmap/special_tile.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+SpecialTile::SpecialTile(const lisp::Lisp* lisp)
+  : passive_message(false), invisible(false),
+    apply_action_north(true), apply_action_east(true),
+    apply_action_south(true), apply_action_west(true)
+{
+  lisp->get("x", pos.x);
+  lisp->get("y", pos.y);
+  lisp->get("invisible-tile", invisible);
+
+  if(!invisible) {
+    std::string spritefile = "";
+    lisp->get("sprite", spritefile);
+    sprite.reset(sprite_manager->create(spritefile));
+  }
+
+  lisp->get("map-message", map_message);
+  lisp->get("passive-message", passive_message);
+  lisp->get("script", script);
+
+  std::string apply_direction;
+  lisp->get("apply-to-direction", apply_direction);
+  if(!apply_direction.empty()) {
+    apply_action_north = false;
+    apply_action_south = false;
+    apply_action_east = false;
+    apply_action_west = false;
+    if(apply_direction.find("north") != std::string::npos)
+      apply_action_north = true;
+    if(apply_direction.find("south") != std::string::npos)
+      apply_action_south = true;
+    if(apply_direction.find("east") != std::string::npos)
+      apply_action_east = true;
+    if(apply_direction.find("west") != std::string::npos)
+      apply_action_west = true;
+  }
+}
+
+SpecialTile::~SpecialTile()
+{
+}
+
+void
+SpecialTile::draw(DrawingContext& context)
+{
+  if(invisible)
+    return;
+
+  sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+SpecialTile::update(float )
+{
+}
+
+}
diff --git a/src/worldmap/special_tile.hpp b/src/worldmap/special_tile.hpp
new file mode 100644 (file)
index 0000000..f32b7b0
--- /dev/null
@@ -0,0 +1,67 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_SPECIAL_TILE_HPP__
+#define __WORLDMAP_SPECIAL_TILE_HPP__
+
+#include <memory>
+#include <string>
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class SpecialTile : public GameObject
+{
+public:
+  SpecialTile(const lisp::Lisp* lisp);
+  virtual ~SpecialTile();
+
+  virtual void draw(DrawingContext& context);
+  virtual void update(float elapsed_time);
+
+  Vector pos;
+
+  /** Sprite to render instead of guessing what image to draw */
+  std::auto_ptr<Sprite> sprite;
+
+  /** Message to show in the Map */
+  std::string map_message;
+  bool passive_message;
+
+  /** Script to execute when tile is touched */
+  std::string script;
+
+  /** Hide special tile */
+  bool invisible;
+
+  /** Only applies actions (ie. passive messages) when going to that direction */
+  bool apply_action_north;
+  bool apply_action_east;
+  bool apply_action_south;
+  bool apply_action_west;
+};
+
+}
+
+#endif
diff --git a/src/worldmap/sprite_change.cpp b/src/worldmap/sprite_change.cpp
new file mode 100644 (file)
index 0000000..4cffd9c
--- /dev/null
@@ -0,0 +1,90 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "sprite_change.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+SpriteChange::SpriteChange(const lisp::Lisp* lisp)
+  : change_on_touch(false), in_stay_action(false)
+{
+  lisp->get("x", pos.x);
+  lisp->get("y", pos.y);
+  lisp->get("change-on-touch", change_on_touch);
+
+  std::string spritefile = "";
+  lisp->get("sprite", spritefile);
+  sprite.reset(sprite_manager->create(spritefile));
+
+  lisp->get("stay-action", stay_action);
+  lisp->get("initial-stay-action", in_stay_action);
+
+  lisp->get("stay-group", stay_group);
+
+  all_sprite_changes.push_back(this);
+}
+
+SpriteChange::~SpriteChange()
+{
+  all_sprite_changes.remove(this);
+}
+
+void
+SpriteChange::draw(DrawingContext& context)
+{
+  if(in_stay_action && stay_action != "") {
+    sprite->set_action(stay_action);
+    sprite->draw(context, pos * 32, LAYER_OBJECTS-1);
+  }
+}
+
+void
+SpriteChange::update(float )
+{
+}
+
+void
+SpriteChange::set_stay_action()
+{
+  in_stay_action = true;
+}
+
+void
+SpriteChange::clear_stay_action()
+{
+  in_stay_action = false;
+
+  // if we are in a stay_group, also clear all stay actions in this group
+  if (stay_group != "") {
+    for (std::list<SpriteChange*>::iterator i = all_sprite_changes.begin(); i != all_sprite_changes.end(); i++) {
+      SpriteChange* sc = *i;
+      if (sc->stay_group != stay_group) continue;
+      sc->in_stay_action = false;
+    }
+  }
+}
+
+std::list<SpriteChange*> SpriteChange::all_sprite_changes;
+
+}
diff --git a/src/worldmap/sprite_change.hpp b/src/worldmap/sprite_change.hpp
new file mode 100644 (file)
index 0000000..4754e27
--- /dev/null
@@ -0,0 +1,86 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_SPRITE_CHANGE_HPP__
+#define __WORLDMAP_SPRITE_CHANGE_HPP__
+
+#include <string>
+#include <memory>
+#include <list>
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class SpriteChange : public GameObject
+{
+public:
+  SpriteChange(const lisp::Lisp* lisp);
+  virtual ~SpriteChange();
+
+  Vector pos;
+  /**
+   * should tuxs sprite change when the tile has been completely entered,
+   * or already when the tile was just touched
+   */
+  bool change_on_touch;
+  /// sprite to change tux image to
+  std::auto_ptr<Sprite> sprite;
+  /**
+   * stay action can be used for objects like boats or cars, if it is
+   * != "" then this sprite will be displayed when tux left the tile towards
+   * another SpriteChange object.
+   */
+  std::string stay_action;
+
+  /**
+   * name of a group in which only one SpriteChange will ever have its stay_action displayed.
+   * Leave empty if you don't care.
+   */
+  std::string stay_group;
+
+  virtual void draw(DrawingContext& context);
+  virtual void update(float elapsed_time);
+
+  /**
+   * Activates the SpriteChange's stay action, if applicable
+   */
+  void set_stay_action();
+
+  /**
+   * Deactivates the SpriteChange's stay action, if applicable
+   */
+  void clear_stay_action();
+
+private:
+  /**
+   * should the stayaction be displayed
+   */
+  bool in_stay_action;
+
+  static std::list<SpriteChange*> all_sprite_changes;
+
+};
+
+}
+
+#endif
diff --git a/src/worldmap/teleporter.cpp b/src/worldmap/teleporter.cpp
new file mode 100644 (file)
index 0000000..d1a7ffb
--- /dev/null
@@ -0,0 +1,57 @@
+//  $Id$
+//
+//  SuperTux - Teleporter Worldmap Tile
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "worldmap/teleporter.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+Teleporter::Teleporter(const lisp::Lisp* lisp)
+  : automatic(false)
+{
+  lisp->get("x", pos.x);
+  lisp->get("y", pos.y);
+
+  std::string spritefile = "";
+  if (lisp->get("sprite", spritefile)) {
+    sprite.reset(sprite_manager->create(spritefile));
+  }
+
+  lisp->get("worldmap", worldmap);
+  lisp->get("spawnpoint", spawnpoint);
+  lisp->get("automatic", automatic);
+  lisp->get("message", message);
+}
+
+void
+Teleporter::draw(DrawingContext& context)
+{
+  if (sprite.get() != 0) sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+Teleporter::update(float )
+{
+}
+
+}
diff --git a/src/worldmap/teleporter.hpp b/src/worldmap/teleporter.hpp
new file mode 100644 (file)
index 0000000..0e5cff3
--- /dev/null
@@ -0,0 +1,63 @@
+//  $Id$
+//
+//  SuperTux - Teleporter Worldmap Tile
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_TELEPORTER_HPP__
+#define __WORLDMAP_TELEPORTER_HPP__
+
+#include <memory>
+#include <string>
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class Teleporter : public GameObject
+{
+public:
+  Teleporter(const lisp::Lisp* lisp);
+
+  virtual void draw(DrawingContext& context);
+  virtual void update(float elapsed_time);
+
+  /** Position (in tiles, not pixels) */
+  Vector pos;
+
+  /** Sprite to render, or 0 for no sprite */
+  std::auto_ptr<Sprite> sprite;
+
+  /** Worldmap filename (relative to data root) to teleport to. Leave empty to use current word */
+  std::string worldmap;
+
+  /** Spawnpoint to teleport to. Leave empty to use "main" or last one */
+  std::string spawnpoint;
+
+  /** true if this teleporter does not need to be activated, but teleports Tux as soon as it's touched */
+  bool automatic;
+
+  /** optional map message to display */
+  std::string message;
+
+};
+
+}
+
+#endif
diff --git a/src/worldmap/tux.cpp b/src/worldmap/tux.cpp
new file mode 100644 (file)
index 0000000..b81fb62
--- /dev/null
@@ -0,0 +1,306 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include "tux.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player_status.hpp"
+#include "worldmap.hpp"
+#include "worldmap/level.hpp"
+#include "special_tile.hpp"
+#include "sprite_change.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+
+namespace WorldMapNS
+{
+
+static const float TUXSPEED = 200;
+static const float map_message_TIME = 2.8f;
+
+Tux::Tux(WorldMap* worldmap_)
+  : worldmap(worldmap_)
+{
+  sprite.reset(sprite_manager->create("images/worldmap/common/tux.sprite"));
+
+  offset = 0;
+  moving = false;
+  direction = D_NONE;
+  input_direction = D_NONE;
+}
+
+Tux::~Tux()
+{
+}
+
+void
+Tux::draw(DrawingContext& context)
+{
+  switch (player_status->bonus) {
+    case GROWUP_BONUS:
+      sprite->set_action(moving ? "large-walking" : "large-stop");
+      break;
+    case FIRE_BONUS:
+      sprite->set_action(moving ? "fire-walking" : "fire-stop");
+      break;
+    case NO_BONUS:
+      sprite->set_action(moving ? "small-walking" : "small-stop");
+      break;
+    default:
+      log_debug << "Bonus type not handled in worldmap." << std::endl;
+      sprite->set_action("large-stop");
+      break;
+  }
+
+  sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+
+Vector
+Tux::get_pos()
+{
+  float x = tile_pos.x * 32;
+  float y = tile_pos.y * 32;
+
+  switch(direction)
+    {
+    case D_WEST:
+      x -= offset - 32;
+      break;
+    case D_EAST:
+      x += offset - 32;
+      break;
+    case D_NORTH:
+      y -= offset - 32;
+      break;
+    case D_SOUTH:
+      y += offset - 32;
+      break;
+    case D_NONE:
+      break;
+    }
+
+  return Vector(x, y);
+}
+
+void
+Tux::stop()
+{
+  offset = 0;
+  direction = D_NONE;
+  input_direction = D_NONE;
+  moving = false;
+}
+
+void
+Tux::set_direction(Direction dir)
+{
+  input_direction = dir;
+}
+
+void
+Tux::tryStartWalking()
+{
+  if (moving)
+    return;
+  if (input_direction == D_NONE)
+    return;
+
+  LevelTile* level = worldmap->at_level();
+
+  // We got a new direction, so lets start walking when possible
+  Vector next_tile;
+  if ((!level || level->solved)
+      && worldmap->path_ok(input_direction, tile_pos, &next_tile)) {
+    tile_pos = next_tile;
+    moving = true;
+    direction = input_direction;
+    back_direction = reverse_dir(direction);
+  } else if (input_direction == back_direction) {
+    moving = true;
+    direction = input_direction;
+    tile_pos = worldmap->get_next_tile(tile_pos, direction);
+    back_direction = reverse_dir(direction);
+  }
+}
+
+bool
+Tux::canWalk(int tile_data, Direction dir)
+{
+  return ((tile_data & Tile::WORLDMAP_NORTH && dir == D_NORTH) ||
+         (tile_data & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) ||
+         (tile_data & Tile::WORLDMAP_EAST && dir == D_EAST) ||
+         (tile_data & Tile::WORLDMAP_WEST && dir == D_WEST));
+}
+
+void
+Tux::tryContinueWalking(float elapsed_time)
+{
+  if (!moving)
+    return;
+
+  // Let tux walk
+  offset += TUXSPEED * elapsed_time;
+
+  // Do nothing if we have not yet reached the next tile
+  if (offset <= 32)
+    return;
+
+  offset -= 32;
+
+  SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
+  if(sprite_change != NULL) {
+    sprite.reset(new Sprite( *(sprite_change->sprite.get()) ));
+    sprite_change->clear_stay_action();
+  }
+
+  // if this is a special_tile with passive_message, display it
+  SpecialTile* special_tile = worldmap->at_special_tile();
+  if(special_tile)
+  {
+    // direction and the apply_action_ are opposites, since they "see"
+    // directions in a different way
+    if((direction == D_NORTH && special_tile->apply_action_south) ||
+                   (direction == D_SOUTH && special_tile->apply_action_north) ||
+                   (direction == D_WEST && special_tile->apply_action_east) ||
+                   (direction == D_EAST && special_tile->apply_action_west))
+    {
+      if(special_tile->passive_message) {
+        worldmap->passive_message = special_tile->map_message;
+        worldmap->passive_message_timer.start(map_message_TIME);
+      } else if(special_tile->script != "") {
+        try {
+          std::istringstream in(special_tile->script);
+          worldmap->run_script(in, "specialtile");
+        } catch(std::exception& e) {
+          log_warning << "Couldn't execute special tile script: " << e.what()
+                      << std::endl;
+        }
+      }
+    }
+  }
+
+  // check if we are at a Teleporter
+  Teleporter* teleporter = worldmap->at_teleporter(tile_pos);
+
+  // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
+  if ((worldmap->at_level())
+      || (worldmap->tile_data_at(tile_pos) & Tile::WORLDMAP_STOP)
+      || (special_tile && !special_tile->passive_message
+                       && special_tile->script == "")
+      || (teleporter)) {
+    if(special_tile && !special_tile->map_message.empty()
+        && !special_tile->passive_message)
+      worldmap->passive_message_timer.start(0);
+    stop();
+    return;
+  }
+
+  // if user wants to change direction, try changing, else guess the direction in which to walk next
+  const int tile_data = worldmap->tile_data_at(tile_pos);
+  if ((direction != input_direction) && canWalk(tile_data, input_direction)) {
+    direction = input_direction;
+    back_direction = reverse_dir(direction);
+  } else {
+    Direction dir = D_NONE;
+    if (tile_data & Tile::WORLDMAP_NORTH && back_direction != D_NORTH)
+      dir = D_NORTH;
+    else if (tile_data & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH)
+      dir = D_SOUTH;
+    else if (tile_data & Tile::WORLDMAP_EAST && back_direction != D_EAST)
+      dir = D_EAST;
+    else if (tile_data & Tile::WORLDMAP_WEST && back_direction != D_WEST)
+      dir = D_WEST;
+
+    if (dir == D_NONE) {
+      // Should never be reached if tiledata is good
+      log_warning << "Could not determine where to walk next" << std::endl;
+      stop();
+      return;
+    }
+
+    direction = dir;
+    input_direction = direction;
+    back_direction = reverse_dir(direction);
+  }
+
+  // Walk automatically to the next tile
+  if(direction == D_NONE)
+    return;
+
+  Vector next_tile;
+  if (!worldmap->path_ok(direction, tile_pos, &next_tile)) {
+    log_debug << "Tilemap data is buggy" << std::endl;
+    stop();
+    return;
+  }
+
+  SpriteChange* next_sprite = worldmap->at_sprite_change(next_tile);
+  if(next_sprite != NULL && next_sprite->change_on_touch) {
+    sprite.reset(new Sprite( *(next_sprite->sprite.get()) ));
+    next_sprite->clear_stay_action();
+  }
+  SpriteChange* last_sprite = worldmap->at_sprite_change(tile_pos);
+  if(last_sprite != NULL && next_sprite != NULL) {
+    log_debug << "Old: " << tile_pos << " New: " << next_tile << std::endl;
+    last_sprite->set_stay_action();
+  }
+
+  tile_pos = next_tile;
+}
+
+void
+Tux::updateInputDirection()
+{
+  if(main_controller->hold(Controller::UP))
+    input_direction = D_NORTH;
+  else if(main_controller->hold(Controller::DOWN))
+    input_direction = D_SOUTH;
+  else if(main_controller->hold(Controller::LEFT))
+    input_direction = D_WEST;
+  else if(main_controller->hold(Controller::RIGHT))
+    input_direction = D_EAST;
+}
+
+void
+Tux::update(float elapsed_time)
+{
+  updateInputDirection();
+  if (moving)
+    tryContinueWalking(elapsed_time);
+  else
+    tryStartWalking();
+}
+
+void
+Tux::setup()
+{
+  // check if we already touch a SpriteChange object
+  SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
+  if(sprite_change != NULL) {
+    sprite.reset(new Sprite( *(sprite_change->sprite.get()) ));
+    sprite_change->clear_stay_action();
+  }
+}
+
+}
diff --git a/src/worldmap/tux.hpp b/src/worldmap/tux.hpp
new file mode 100644 (file)
index 0000000..4c36042
--- /dev/null
@@ -0,0 +1,76 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef __WORLDMAP_TUX_HPP__
+#define __WORLDMAP_TUX_HPP__
+
+#include <memory>
+#include "game_object.hpp"
+#include "worldmap.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class WorldMap;
+
+class Tux : public GameObject
+{
+public:
+  Direction back_direction;
+private:
+  WorldMap* worldmap;
+  std::auto_ptr<Sprite> sprite;
+  Controller* controller;
+
+  Direction input_direction;
+  Direction direction;
+  Vector tile_pos;
+  /** Length by which tux is away from its current tile, length is in
+      input_direction direction */
+  float offset;
+  bool  moving;
+
+  void stop();
+
+  bool canWalk(int tile_data, Direction dir); /**< check if we can leave a tile (with given "tile_data") in direction "dir" */
+  void updateInputDirection(); /**< if controller was pressed, update input_direction */
+  void tryStartWalking(); /**< try starting to walk in input_direction */
+  void tryContinueWalking(float elapsed_time); /**< try to continue walking in current direction */
+
+public:
+  Tux(WorldMap* worldmap_);
+  ~Tux();
+
+  void setup(); /**< called prior to first update */
+  void draw(DrawingContext& context);
+  void update(float elapsed_time);
+
+  void set_direction(Direction dir);
+
+  bool is_moving() const { return moving; }
+  Vector get_pos();
+  Vector get_tile_pos() const { return tile_pos; }
+  void  set_tile_pos(Vector p) { tile_pos = p; }
+};
+
+}
+
+#endif
diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp
new file mode 100644 (file)
index 0000000..2f5d3aa
--- /dev/null
@@ -0,0 +1,1113 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <cassert>
+#include <stdexcept>
+#include <sstream>
+#include <unistd.h>
+#include <physfs.h>
+
+#include "worldmap.hpp"
+
+#include "gettext.hpp"
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "shrinkfade.hpp"
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/writer.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "worldmap.hpp"
+#include "resources.hpp"
+#include "log.hpp"
+#include "world.hpp"
+#include "player_status.hpp"
+#include "textscroller.hpp"
+#include "main.hpp"
+#include "spawn_point.hpp"
+#include "file_system.hpp"
+#include "gui/menu.hpp"
+#include "gui/mousecursor.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "object/background.hpp"
+#include "object/tilemap.hpp"
+#include "options_menu.hpp"
+#include "scripting/squirrel_error.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "worldmap/level.hpp"
+#include "worldmap/special_tile.hpp"
+#include "worldmap/tux.hpp"
+#include "worldmap/sprite_change.hpp"
+
+namespace WorldMapNS {
+
+enum WorldMapMenuIDs {
+  MNID_RETURNWORLDMAP,
+  MNID_QUITWORLDMAP
+};
+
+WorldMap* WorldMap::current_ = NULL;
+
+Direction reverse_dir(Direction direction)
+{
+  switch(direction)
+    {
+    case D_WEST:
+      return D_EAST;
+    case D_EAST:
+      return D_WEST;
+    case D_NORTH:
+      return D_SOUTH;
+    case D_SOUTH:
+      return D_NORTH;
+    case D_NONE:
+      return D_NONE;
+    }
+  return D_NONE;
+}
+
+std::string
+direction_to_string(Direction direction)
+{
+  switch(direction)
+    {
+    case D_WEST:
+      return "west";
+    case D_EAST:
+      return "east";
+    case D_NORTH:
+      return "north";
+    case D_SOUTH:
+      return "south";
+    default:
+      return "none";
+    }
+}
+
+Direction
+string_to_direction(const std::string& directory)
+{
+  if (directory == "west")
+    return D_WEST;
+  else if (directory == "east")
+    return D_EAST;
+  else if (directory == "north")
+    return D_NORTH;
+  else if (directory == "south")
+    return D_SOUTH;
+  else if (directory == "none")
+    return D_NONE;
+  else {
+    log_warning << "unknown direction: \"" << directory << "\"" << std::endl;
+    return D_NONE;
+  }
+}
+
+//---------------------------------------------------------------------------
+
+WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint)
+  : tux(0), ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), force_spawnpoint(force_spawnpoint), in_level(false)
+{
+  tile_manager.reset(new TileManager("images/worldmap.strf"));
+
+  tux = new Tux(this);
+  add_object(tux);
+
+  name = "<no title>";
+  music = "music/salcon.ogg";
+
+  total_stats.reset();
+
+  worldmap_menu.reset(new Menu());
+  worldmap_menu->add_label(_("Pause"));
+  worldmap_menu->add_hl();
+  worldmap_menu->add_entry(MNID_RETURNWORLDMAP, _("Continue"));
+  worldmap_menu->add_submenu(_("Options"), get_options_menu());
+  worldmap_menu->add_hl();
+  worldmap_menu->add_entry(MNID_QUITWORLDMAP, _("Quit World"));
+
+  // create a new squirrel table for the worldmap
+  using namespace Scripting;
+
+  sq_collectgarbage(global_vm);
+  sq_newtable(global_vm);
+  sq_pushroottable(global_vm);
+  if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't set worldmap_table delegate");
+
+  sq_resetobject(&worldmap_table);
+  if(SQ_FAILED(sq_getstackobj(global_vm, -1, &worldmap_table)))
+    throw Scripting::SquirrelError(global_vm, "Couldn't get table from stack");
+
+  sq_addref(global_vm, &worldmap_table);
+  sq_pop(global_vm, 1);
+  
+  // load worldmap objects
+  load(filename);
+}
+
+WorldMap::~WorldMap()
+{
+  using namespace Scripting;
+
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ++i) {
+    GameObject* object = *i;
+    try_unexpose(object);
+    object->unref();
+  }
+
+  for(SpawnPoints::iterator i = spawn_points.begin();
+      i != spawn_points.end(); ++i) {
+    delete *i;
+  }
+
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ++i) {
+    HSQOBJECT& object = *i;
+    sq_release(global_vm, &object);
+  }
+  sq_release(global_vm, &worldmap_table);
+
+  sq_collectgarbage(global_vm);
+
+  if(current_ == this)
+    current_ = NULL;
+}
+
+void
+WorldMap::add_object(GameObject* object)
+{
+  TileMap* tilemap = dynamic_cast<TileMap*> (object);
+  if(tilemap != 0 && tilemap->is_solid()) {
+    solid_tilemaps.push_back(tilemap);
+  }
+
+  object->ref();
+  try_expose(object);
+  game_objects.push_back(object);
+}
+
+void
+WorldMap::try_expose(GameObject* object)
+{
+  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+  if(interface != NULL) {
+    HSQUIRRELVM vm = Scripting::global_vm;
+    sq_pushobject(vm, worldmap_table);
+    interface->expose(vm, -1);
+    sq_pop(vm, 1);
+  }
+}
+
+void
+WorldMap::try_unexpose(GameObject* object)
+{
+  ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+  if(interface != NULL) {
+    HSQUIRRELVM vm = Scripting::global_vm;
+    SQInteger oldtop = sq_gettop(vm);
+    sq_pushobject(vm, worldmap_table);
+    try {
+      interface->unexpose(vm, -1);
+    } catch(std::exception& e) {
+      log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+    }
+    sq_settop(vm, oldtop);
+  }
+}
+
+void
+WorldMap::move_to_spawnpoint(const std::string& spawnpoint)
+{
+  for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) {
+    SpawnPoint* sp = *i;
+    if(sp->name == spawnpoint) {
+      Vector p = sp->pos;
+      tux->set_tile_pos(p);
+      tux->set_direction(sp->auto_dir);
+      return;
+    }
+  }
+  log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
+  if (spawnpoint != "main") {
+    move_to_spawnpoint("main");
+  }
+}
+
+void
+WorldMap::change(const std::string& filename, const std::string& force_spawnpoint)
+{
+  main_loop->exit_screen();
+  main_loop->push_screen(new WorldMap(filename, force_spawnpoint));
+}
+
+void
+WorldMap::load(const std::string& filename)
+{
+  map_filename = filename;
+  levels_path = FileSystem::dirname(map_filename);
+
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(map_filename);
+
+    const lisp::Lisp* lisp = root->get_lisp("supertux-level");
+    if(!lisp)
+      throw std::runtime_error("file isn't a supertux-level file.");
+
+    lisp->get("name", name);
+
+    const lisp::Lisp* sector = lisp->get_lisp("sector");
+    if(!sector)
+      throw std::runtime_error("No sector sepcified in worldmap file.");
+
+    lisp::ListIterator iter(sector);
+    while(iter.next()) {
+      if(iter.item() == "tilemap") {
+        add_object(new TileMap(*(iter.lisp()), tile_manager.get()));
+      } else if(iter.item() == "background") {
+        add_object(new Background(*(iter.lisp())));
+      } else if(iter.item() == "music") {
+        iter.value()->get(music);
+      } else if(iter.item() == "init-script") {
+        iter.value()->get(init_script);
+      } else if(iter.item() == "worldmap-spawnpoint") {
+        SpawnPoint* sp = new SpawnPoint(iter.lisp());
+        spawn_points.push_back(sp);
+      } else if(iter.item() == "level") {
+        LevelTile* level = new LevelTile(levels_path, iter.lisp());
+        levels.push_back(level);
+        add_object(level);
+      } else if(iter.item() == "special-tile") {
+        SpecialTile* special_tile = new SpecialTile(iter.lisp());
+        special_tiles.push_back(special_tile);
+        add_object(special_tile);
+      } else if(iter.item() == "sprite-change") {
+        SpriteChange* sprite_change = new SpriteChange(iter.lisp());
+        sprite_changes.push_back(sprite_change);
+        add_object(sprite_change);
+      } else if(iter.item() == "teleporter") {
+        Teleporter* teleporter = new Teleporter(iter.lisp());
+        teleporters.push_back(teleporter);
+        add_object(teleporter);
+      } else if(iter.item() == "ambient-light") {
+        std::vector<float> vColor;
+        sector->get_vector( "ambient-light", vColor );
+        if(vColor.size() < 3) {
+          log_warning << "(ambient-light) requires a color as argument" << std::endl;
+        } else {
+          ambient_light = Color( vColor );
+        }
+      } else if(iter.item() == "name") {
+        // skip
+      } else {
+        log_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl;
+      }
+    }
+    if(solid_tilemaps.size() == 0)
+      throw std::runtime_error("No solid tilemap specified");
+
+    move_to_spawnpoint("main");
+
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when parsing worldmap '" << map_filename << "': " <<
+      e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
+
+void
+WorldMap::get_level_title(LevelTile& level)
+{
+  /** get special_tile's title */
+  level.title = "<no title>";
+
+  try {
+    lisp::Parser parser;
+    const lisp::Lisp* root = parser.parse(levels_path + level.get_name());
+
+    const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
+    if(!level_lisp)
+      return;
+
+    level_lisp->get("name", level.title);
+  } catch(std::exception& e) {
+    log_warning << "Problem when reading leveltitle: " << e.what() << std::endl;
+    return;
+  }
+}
+
+void WorldMap::calculate_total_stats()
+{
+  total_stats.zero();
+  for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+    LevelTile* level = *i;
+    if (level->solved) {
+      total_stats += level->statistics;
+    }
+  }
+}
+
+void
+WorldMap::on_escape_press()
+{
+  // Show or hide the menu
+  if(!Menu::current()) {
+    Menu::set_current(worldmap_menu.get());
+    tux->set_direction(D_NONE);  // stop tux movement when menu is called
+  } else {
+    Menu::set_current(NULL);
+  }
+}
+
+Vector
+WorldMap::get_next_tile(Vector pos, Direction direction)
+{
+  switch(direction) {
+    case D_WEST:
+      pos.x -= 1;
+      break;
+    case D_EAST:
+      pos.x += 1;
+      break;
+    case D_NORTH:
+      pos.y -= 1;
+      break;
+    case D_SOUTH:
+      pos.y += 1;
+      break;
+    case D_NONE:
+      break;
+  }
+  return pos;
+}
+
+bool
+WorldMap::path_ok(Direction direction, const Vector& old_pos, Vector* new_pos)
+{
+  *new_pos = get_next_tile(old_pos, direction);
+
+  if (!(new_pos->x >= 0 && new_pos->x < get_width()
+        && new_pos->y >= 0 && new_pos->y < get_height()))
+    { // New position is outsite the tilemap
+      return false;
+    }
+  else
+    { // Check if the tile allows us to go to new_pos
+      int old_tile_data = tile_data_at(old_pos);
+      int new_tile_data = tile_data_at(*new_pos);
+      switch(direction)
+        {
+        case D_WEST:
+          return (old_tile_data & Tile::WORLDMAP_WEST
+              && new_tile_data & Tile::WORLDMAP_EAST);
+
+        case D_EAST:
+          return (old_tile_data & Tile::WORLDMAP_EAST
+              && new_tile_data & Tile::WORLDMAP_WEST);
+
+        case D_NORTH:
+          return (old_tile_data & Tile::WORLDMAP_NORTH
+              && new_tile_data & Tile::WORLDMAP_SOUTH);
+
+        case D_SOUTH:
+          return (old_tile_data & Tile::WORLDMAP_SOUTH
+              && new_tile_data & Tile::WORLDMAP_NORTH);
+
+        case D_NONE:
+          assert(!"path_ok() can't walk if direction is NONE");
+        }
+      return false;
+    }
+}
+
+void
+WorldMap::finished_level(Level* gamelevel)
+{
+  // TODO use Level* parameter here?
+  LevelTile* level = at_level();
+
+  bool old_level_state = level->solved;
+  level->solved = true;
+  level->sprite->set_action("solved");
+
+  // deal with statistics
+  level->statistics.merge(gamelevel->stats);
+  calculate_total_stats();
+
+  save_state();
+
+  if (old_level_state != level->solved) {
+    // Try to detect the next direction to which we should walk
+    // FIXME: Mostly a hack
+    Direction dir = D_NONE;
+
+    int dirdata = available_directions_at(tux->get_tile_pos());
+    // first, test for crossroads
+    if (dirdata == Tile::WORLDMAP_CNSE ||
+               dirdata == Tile::WORLDMAP_CNSW ||
+               dirdata == Tile::WORLDMAP_CNEW ||
+               dirdata == Tile::WORLDMAP_CSEW ||
+               dirdata == Tile::WORLDMAP_CNSEW)
+      dir = D_NONE;
+    else if (dirdata & Tile::WORLDMAP_NORTH
+        && tux->back_direction != D_NORTH)
+      dir = D_NORTH;
+    else if (dirdata & Tile::WORLDMAP_SOUTH
+        && tux->back_direction != D_SOUTH)
+      dir = D_SOUTH;
+    else if (dirdata & Tile::WORLDMAP_EAST
+        && tux->back_direction != D_EAST)
+      dir = D_EAST;
+    else if (dirdata & Tile::WORLDMAP_WEST
+        && tux->back_direction != D_WEST)
+      dir = D_WEST;
+
+    if (dir != D_NONE) {
+      tux->set_direction(dir);
+    }
+  }
+
+  if (level->extro_script != "") {
+    try {
+      std::istringstream in(level->extro_script);
+      run_script(in, "worldmap:extro_script");
+    } catch(std::exception& e) {
+      log_fatal << "Couldn't run level-extro-script: " << e.what() << std::endl;
+    }
+  }
+}
+
+void
+WorldMap::update(float delta)
+{
+  if(!in_level) {
+    Menu* menu = Menu::current();
+    if(menu != NULL) {
+      menu->update();
+
+      if(menu == worldmap_menu.get()) {
+        switch (worldmap_menu->check())
+        {
+          case MNID_RETURNWORLDMAP: // Return to game
+            Menu::set_current(0);
+            break;
+          case MNID_QUITWORLDMAP: // Quit Worldmap
+            main_loop->exit_screen();
+            break;
+        }
+      }
+
+      return;
+    }
+
+    // update GameObjects
+    for(size_t i = 0; i < game_objects.size(); ++i) {
+      GameObject* object = game_objects[i];
+      object->update(delta);
+    }
+
+    // remove old GameObjects
+    for(GameObjects::iterator i = game_objects.begin();
+        i != game_objects.end(); ) {
+      GameObject* object = *i;
+      if(!object->is_valid()) {
+        try_unexpose(object);
+        object->unref();
+        i = game_objects.erase(i);
+      } else {
+        ++i;
+      }
+    }
+
+    /* update solid_tilemaps list */
+    //FIXME: this could be more efficient
+    solid_tilemaps.clear();
+    for(std::vector<GameObject*>::iterator i = game_objects.begin();
+        i != game_objects.end(); ++i)
+    {
+      TileMap* tm = dynamic_cast<TileMap*>(*i);
+      if (!tm) continue;
+      if (tm->is_solid()) solid_tilemaps.push_back(tm);
+    }
+
+    // position "camera"
+    Vector tux_pos = tux->get_pos();
+    camera_offset.x = tux_pos.x - SCREEN_WIDTH/2;
+    camera_offset.y = tux_pos.y - SCREEN_HEIGHT/2;
+
+    if (camera_offset.x < 0)
+      camera_offset.x = 0;
+    if (camera_offset.y < 0)
+      camera_offset.y = 0;
+
+    if (camera_offset.x > (int)get_width()*32 - SCREEN_WIDTH)
+      camera_offset.x = (int)get_width()*32 - SCREEN_WIDTH;
+    if (camera_offset.y > (int)get_height()*32 - SCREEN_HEIGHT)
+      camera_offset.y = (int)get_height()*32 - SCREEN_HEIGHT;
+
+    if (int(get_width()*32) < SCREEN_WIDTH)
+      camera_offset.x = get_width()*16.0 - SCREEN_WIDTH/2.0;
+    if (int(get_height()*32) < SCREEN_HEIGHT)
+      camera_offset.y = get_height()*16.0 - SCREEN_HEIGHT/2.0;
+
+    // handle input
+    bool enter_level = false;
+    if(main_controller->pressed(Controller::ACTION)
+        || main_controller->pressed(Controller::JUMP)
+        || main_controller->pressed(Controller::MENU_SELECT)) {
+      /* some people define UP and JUMP on the same key... */
+      if(!main_controller->pressed(Controller::UP))
+           enter_level = true;
+       }
+    if(main_controller->pressed(Controller::PAUSE_MENU))
+      on_escape_press();
+
+    // check for teleporters
+    Teleporter* teleporter = at_teleporter(tux->get_tile_pos());
+    if (teleporter && (teleporter->automatic || (enter_level && (!tux->is_moving())))) {
+      enter_level = false;
+      if (teleporter->worldmap != "") {
+        change(teleporter->worldmap, teleporter->spawnpoint);
+      } else {
+        // TODO: an animation, camera scrolling or a fading would be a nice touch
+        sound_manager->play("sounds/warp.wav");
+        tux->back_direction = D_NONE;
+        move_to_spawnpoint(teleporter->spawnpoint);
+      }
+    }
+
+    // check for auto-play levels
+    LevelTile* level = at_level();
+    if (level && (level->auto_play) && (!level->solved) && (!tux->is_moving())) {
+      enter_level = true;
+    }
+
+    if (enter_level && !tux->is_moving())
+      {
+        /* Check level action */
+        LevelTile* level = at_level();
+        if (!level) {
+          //Respawn if player on a tile with no level and nowhere to go.
+          int tile_data = tile_data_at(tux->get_tile_pos());
+          if(!( tile_data & ( Tile::WORLDMAP_NORTH |  Tile::WORLDMAP_SOUTH | Tile::WORLDMAP_WEST | Tile::WORLDMAP_EAST ))){
+            log_warning << "Player at illegal position " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << " respawning." << std::endl;
+            move_to_spawnpoint("main");
+            return;
+          }
+          log_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
+          return;
+        }
+
+        if (level->pos == tux->get_tile_pos()) {
+          try {
+            Vector shrinkpos = Vector(level->pos.x*32 + 16 - camera_offset.x,
+                                      level->pos.y*32 + 16 - camera_offset.y);
+            std::string levelfile = levels_path + level->get_name();
+
+            // update state and savegame
+            save_state();
+
+            main_loop->push_screen(new GameSession(levelfile, &level->statistics),
+                                   new ShrinkFade(shrinkpos, 0.5));
+            in_level = true;
+          } catch(std::exception& e) {
+            log_fatal << "Couldn't load level: " << e.what() << std::endl;
+          }
+        }
+      }
+    else
+      {
+  //      tux->set_direction(input_direction);
+      }
+  }
+}
+
+int
+WorldMap::tile_data_at(Vector p)
+{
+  int dirs = 0;
+
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* tilemap = *i;
+    const Tile* tile = tilemap->get_tile((int)p.x, (int)p.y);
+    int dirdata = tile->getData();
+    dirs |= dirdata;
+  }
+
+  return dirs;
+}
+
+int
+WorldMap::available_directions_at(Vector p)
+{
+  return tile_data_at(p) & Tile::WORLDMAP_DIR_MASK;
+}
+
+LevelTile*
+WorldMap::at_level()
+{
+  for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+    LevelTile* level = *i;
+    if (level->pos == tux->get_tile_pos())
+      return level;
+  }
+
+  return NULL;
+}
+
+SpecialTile*
+WorldMap::at_special_tile()
+{
+  for(SpecialTiles::iterator i = special_tiles.begin();
+      i != special_tiles.end(); ++i) {
+    SpecialTile* special_tile = *i;
+    if (special_tile->pos == tux->get_tile_pos())
+      return special_tile;
+  }
+
+  return NULL;
+}
+
+SpriteChange*
+WorldMap::at_sprite_change(const Vector& pos)
+{
+  for(SpriteChanges::iterator i = sprite_changes.begin();
+      i != sprite_changes.end(); ++i) {
+    SpriteChange* sprite_change = *i;
+    if(sprite_change->pos == pos)
+      return sprite_change;
+  }
+
+  return NULL;
+}
+
+Teleporter*
+WorldMap::at_teleporter(const Vector& pos)
+{
+  for(std::vector<Teleporter*>::iterator i = teleporters.begin(); i != teleporters.end(); ++i) {
+    Teleporter* teleporter = *i;
+    if(teleporter->pos == pos) return teleporter;
+  }
+
+  return NULL;
+}
+
+void
+WorldMap::draw(DrawingContext& context)
+{
+  if (int(get_width()*32) < SCREEN_WIDTH || int(get_height()*32) < SCREEN_HEIGHT)
+    context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+      Color(0.0f, 0.0f, 0.0f, 1.0f), LAYER_BACKGROUND0);
+
+  context.set_ambient_color( ambient_light );
+  context.push_transform();
+  context.set_translation(camera_offset);
+
+  for(GameObjects::iterator i = game_objects.begin();
+      i != game_objects.end(); ++i) {
+    GameObject* object = *i;
+    object->draw(context);
+  }
+
+/*
+  // FIXME: make this a runtime switch similar to draw_collrects/show_collrects?
+  // draw visual indication of possible walk directions
+  static int flipme = 0; 
+  if (flipme++ & 0x04)
+  for (int x = 0; x < get_width(); x++) {
+    for (int y = 0; y < get_height(); y++) {
+      int data = tile_data_at(Vector(x,y));
+      int px = x * 32;
+      int py = y * 32;
+      const int W = 4;
+      if (data & Tile::WORLDMAP_NORTH)    context.draw_filled_rect(Rect(px + 16-W, py       , px + 16+W, py + 16-W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+      if (data & Tile::WORLDMAP_SOUTH)    context.draw_filled_rect(Rect(px + 16-W, py + 16+W, px + 16+W, py + 32  ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+      if (data & Tile::WORLDMAP_EAST)     context.draw_filled_rect(Rect(px + 16+W, py + 16-W, px + 32  , py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+      if (data & Tile::WORLDMAP_WEST)     context.draw_filled_rect(Rect(px       , py + 16-W, px + 16-W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+      if (data & Tile::WORLDMAP_DIR_MASK) context.draw_filled_rect(Rect(px + 16-W, py + 16-W, px + 16+W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+      if (data & Tile::WORLDMAP_STOP)     context.draw_filled_rect(Rect(px + 4   , py + 4   , px + 28  , py + 28  ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+    }
+  }
+*/
+
+  draw_status(context);
+  context.pop_transform();
+}
+
+void
+WorldMap::draw_status(DrawingContext& context)
+{
+  context.push_transform();
+  context.set_translation(Vector(0, 0));
+
+  player_status->draw(context);
+
+  if (!tux->is_moving()) {
+    for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+      LevelTile* level = *i;
+
+      if (level->pos == tux->get_tile_pos()) {
+        if(level->title == "")
+          get_level_title(*level);
+
+        context.draw_text(white_text, level->title,
+                          Vector(SCREEN_WIDTH/2,
+                                 SCREEN_HEIGHT - white_text->get_height() - 30),
+                          ALIGN_CENTER, LAYER_FOREGROUND1);
+
+        // if level is solved, draw level picture behind stats
+        /*
+        if (level->solved) {
+          if (const Surface* picture = level->get_picture()) {
+            Vector pos = Vector(SCREEN_WIDTH - picture->get_width(), SCREEN_HEIGHT - picture->get_height());
+            context.push_transform();
+            context.set_alpha(0.5);
+            context.draw_surface(picture, pos, LAYER_FOREGROUND1-1);
+            context.pop_transform();
+          }
+        }
+        */
+
+        level->statistics.draw_worldmap_info(context);
+        break;
+      }
+    }
+
+    for(SpecialTiles::iterator i = special_tiles.begin();
+        i != special_tiles.end(); ++i) {
+      SpecialTile* special_tile = *i;
+
+      if (special_tile->pos == tux->get_tile_pos()) {
+        /* Display an in-map message in the map, if any as been selected */
+        if(!special_tile->map_message.empty() && !special_tile->passive_message)
+          context.draw_text(gold_text, special_tile->map_message,
+              Vector(SCREEN_WIDTH/2,
+                SCREEN_HEIGHT - white_text->get_height() - 60),
+              ALIGN_CENTER, LAYER_FOREGROUND1);
+        break;
+      }
+    }
+
+    // display teleporter messages
+    Teleporter* teleporter = at_teleporter(tux->get_tile_pos());
+    if (teleporter && (teleporter->message != "")) {
+      Vector pos = Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 30);
+      context.draw_text(white_text, teleporter->message, pos, ALIGN_CENTER, LAYER_FOREGROUND1);
+    }
+
+  }
+
+  /* Display a passive message in the map, if needed */
+  if(passive_message_timer.started())
+    context.draw_text(gold_text, passive_message,
+            Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60),
+            ALIGN_CENTER, LAYER_FOREGROUND1);
+
+  context.pop_transform();
+}
+
+void
+WorldMap::setup()
+{
+  sound_manager->play_music(music);
+  Menu::set_current(NULL);
+
+  current_ = this;
+  load_state();
+
+  // if force_spawnpoint was set, move Tux there, then clear force_spawnpoint
+  if (force_spawnpoint != "") {
+    move_to_spawnpoint(force_spawnpoint);
+    force_spawnpoint = "";
+  }
+
+  tux->setup();
+
+  // register worldmap_table as worldmap in scripting
+  using namespace Scripting;
+
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "worldmap", -1);
+  sq_pushobject(global_vm, worldmap_table);
+  if(SQ_FAILED(sq_createslot(global_vm, -3)))
+    throw SquirrelError(global_vm, "Couldn't set worldmap in roottable");
+  sq_pop(global_vm, 1);
+
+  if(init_script != "") {
+    std::istringstream in(init_script);
+    run_script(in, "WorldMap::init");
+  }
+}
+
+void
+WorldMap::leave()
+{
+  using namespace Scripting;
+
+  // save state of world and player
+  save_state();
+
+  // remove worldmap_table from roottable
+  sq_pushroottable(global_vm);
+  sq_pushstring(global_vm, "worldmap", -1);
+  if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+    throw SquirrelError(global_vm, "Couldn't unset worldmap in roottable");
+  sq_pop(global_vm, 1);
+}
+
+void
+WorldMap::save_state()
+{
+  using namespace Scripting;
+
+  HSQUIRRELVM vm = global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try {
+    // get state table
+    sq_pushroottable(vm);
+    sq_pushstring(vm, "state", -1);
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get state table");
+
+    // get or create worlds table
+    sq_pushstring(vm, "worlds", -1);
+    if(SQ_FAILED(sq_get(vm, -2))) {
+      sq_pushstring(vm, "worlds", -1);
+      sq_newtable(vm);
+      if(SQ_FAILED(sq_createslot(vm, -3)))
+        throw Scripting::SquirrelError(vm, "Couldn't create state.worlds");
+
+      sq_pushstring(vm, "worlds", -1);
+      if(SQ_FAILED(sq_get(vm, -2)))
+        throw Scripting::SquirrelError(vm, "Couldn't create.get state.worlds");
+    }
+
+    sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+    if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+      sq_pop(vm, 1);
+
+    // construct new table for this worldmap
+    sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+    sq_newtable(vm);
+
+    // store tux
+    sq_pushstring(vm, "tux", -1);
+    sq_newtable(vm);
+
+    store_float(vm, "x", tux->get_tile_pos().x);
+    store_float(vm, "y", tux->get_tile_pos().y);
+    store_string(vm, "back", direction_to_string(tux->back_direction));
+
+    sq_createslot(vm, -3);
+
+    // levels...
+    sq_pushstring(vm, "levels", -1);
+    sq_newtable(vm);
+
+    for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+      LevelTile* level = *i;
+
+         sq_pushstring(vm, level->get_name().c_str(), -1);
+         sq_newtable(vm);
+
+         store_bool(vm, "solved", level->solved);
+         level->statistics.serialize_to_squirrel(vm);
+
+         sq_createslot(vm, -3);
+    }
+
+    sq_createslot(vm, -3);
+
+    // overall statistics...
+    total_stats.serialize_to_squirrel(vm);
+
+    // push world into worlds table
+    sq_createslot(vm, -3);
+  } catch(std::exception& ) {
+    sq_settop(vm, oldtop);
+  }
+
+  sq_settop(vm, oldtop);
+
+  if(World::current() != NULL)
+    World::current()->save_state();
+}
+
+void
+WorldMap::load_state()
+{
+  using namespace Scripting;
+
+  HSQUIRRELVM vm = global_vm;
+  int oldtop = sq_gettop(vm);
+
+  try {
+    // get state table
+    sq_pushroottable(vm);
+    sq_pushstring(vm, "state", -1);
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get state table");
+
+    // get worlds table
+    sq_pushstring(vm, "worlds", -1);
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get state.worlds");
+
+    // get table for our world
+    sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get state.worlds.mapfilename");
+
+    // load tux
+    sq_pushstring(vm, "tux", -1);
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get tux");
+
+    Vector p;
+    p.x = read_float(vm, "x");
+    p.y = read_float(vm, "y");
+    std::string back_str = read_string(vm, "back");
+    tux->back_direction = string_to_direction(back_str);
+    tux->set_tile_pos(p);
+
+    sq_pop(vm, 1);
+
+    // load levels
+    sq_pushstring(vm, "levels", -1);
+    if(SQ_FAILED(sq_get(vm, -2)))
+      throw Scripting::SquirrelError(vm, "Couldn't get levels");
+
+    for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+      LevelTile* level = *i;
+      sq_pushstring(vm, level->get_name().c_str(), -1);
+      if(SQ_SUCCEEDED(sq_get(vm, -2))) {
+        level->solved = read_bool(vm, "solved");
+        level->sprite->set_action(level->solved ? "solved" : "default");
+        level->statistics.unserialize_from_squirrel(vm);
+        sq_pop(vm, 1);
+      }
+    }
+
+    // leave state table
+    sq_pop(vm, 1);
+
+    // load overall statistics
+    total_stats.unserialize_from_squirrel(vm);
+
+  } catch(std::exception& e) {
+    log_debug << "Not loading worldmap state: " << e.what() << std::endl;
+  }
+  sq_settop(vm, oldtop);
+
+  in_level = false;
+}
+
+size_t
+WorldMap::level_count()
+{
+  return levels.size();
+}
+
+size_t
+WorldMap::solved_level_count()
+{
+  size_t count = 0;
+  for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+    LevelTile* level = *i;
+
+    if(level->solved)
+      count++;
+  }
+
+  return count;
+}
+
+HSQUIRRELVM
+WorldMap::run_script(std::istream& in, const std::string& sourcename)
+{
+  using namespace Scripting;
+
+  // garbage collect thread list
+  for(ScriptList::iterator i = scripts.begin();
+      i != scripts.end(); ) {
+    HSQOBJECT& object = *i;
+    HSQUIRRELVM vm = object_to_vm(object);
+
+    if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+      sq_release(global_vm, &object);
+      i = scripts.erase(i);
+      continue;
+    }
+
+    ++i;
+  }
+
+  HSQOBJECT object = create_thread(global_vm);
+  scripts.push_back(object);
+
+  HSQUIRRELVM vm = object_to_vm(object);
+
+  // set worldmap_table as roottable for the thread
+  sq_pushobject(vm, worldmap_table);
+  sq_setroottable(vm);
+
+  compile_and_run(vm, in, sourcename);
+
+  return vm;
+}
+
+float
+WorldMap::get_width() const
+{
+  float width = 0;
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    if (solids->get_width() > width) width = solids->get_width();
+  }
+  return width;
+}
+
+float
+WorldMap::get_height() const
+{
+  float height = 0;
+  for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+    TileMap* solids = *i;
+    if (solids->get_height() > height) height = solids->get_height();
+  }
+  return height;
+}
+
+} // namespace WorldMapNS
diff --git a/src/worldmap/worldmap.hpp b/src/worldmap/worldmap.hpp
new file mode 100644 (file)
index 0000000..bfcf960
--- /dev/null
@@ -0,0 +1,224 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+//  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_WORLDMAP_H
+#define SUPERTUX_WORLDMAP_H
+
+#include <vector>
+#include <string>
+
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "control/controller.hpp"
+#include "statistics.hpp"
+#include "timer.hpp"
+#include "screen.hpp"
+#include "tile_manager.hpp"
+#include "game_object.hpp"
+#include "console.hpp"
+#include "../level.hpp"
+#include "worldmap/special_tile.hpp"
+#include "worldmap/sprite_change.hpp"
+#include "worldmap/teleporter.hpp"
+#include "worldmap/spawn_point.hpp"
+#include "worldmap/direction.hpp"
+
+class Sprite;
+class Menu;
+class GameObject;
+class TileMap;
+
+namespace WorldMapNS {
+
+class Tux;
+class LevelTile;
+class SpecialTile;
+class SpriteChange;
+
+// For one way tiles
+enum {
+  BOTH_WAYS,
+  NORTH_SOUTH_WAY,
+  SOUTH_NORTH_WAY,
+  EAST_WEST_WAY,
+  WEST_EAST_WAY
+};
+
+std::string direction_to_string(Direction d);
+Direction   string_to_direction(const std::string& d);
+Direction reverse_dir(Direction d);
+
+/**
+ * Screen that displays a worldmap
+ */
+class WorldMap : public Screen
+{
+private:
+  Tux* tux;
+
+  static WorldMap* current_;
+
+  std::auto_ptr<Menu> worldmap_menu;
+
+  Vector camera_offset;
+
+  std::string name;
+  std::string music;
+  std::string init_script;
+
+  typedef std::vector<GameObject*> GameObjects;
+  GameObjects game_objects;
+  std::list<TileMap*> solid_tilemaps;
+
+  std::auto_ptr<TileManager> tile_manager;
+
+public:
+  /** Variables to deal with the passive map messages */
+  Timer passive_message_timer;
+  std::string passive_message;
+
+private:
+  std::string map_filename;
+  std::string levels_path;
+
+  typedef std::vector<SpecialTile*> SpecialTiles;
+  SpecialTiles special_tiles;
+  typedef std::vector<LevelTile*> LevelTiles;
+  LevelTiles levels;
+  typedef std::vector<SpriteChange*> SpriteChanges;
+  SpriteChanges sprite_changes;
+  typedef std::vector<SpawnPoint*> SpawnPoints;
+  SpawnPoints spawn_points;
+  std::vector<Teleporter*> teleporters;
+
+  Statistics total_stats;
+
+  HSQOBJECT worldmap_table;
+  typedef std::vector<HSQOBJECT> ScriptList;
+  ScriptList scripts;
+
+  Color ambient_light;
+  std::string force_spawnpoint; /**< if set, spawnpoint will be forced to this value */
+
+  bool in_level;
+
+public:
+  WorldMap(const std::string& filename, const std::string& force_spawnpoint = "");
+  ~WorldMap();
+
+  void add_object(GameObject* object);
+  
+  void try_expose(GameObject* object);
+  void try_unexpose(GameObject* object);
+
+  static WorldMap* current()
+  { return current_; }
+
+  virtual void setup();
+  virtual void leave();
+
+  /** Update worldmap state */
+  virtual void update(float delta);
+  /** Draw worldmap */
+  virtual void draw(DrawingContext& context);
+
+  Vector get_next_tile(Vector pos, Direction direction);
+
+  /**
+   * gets a bitfield of Tile::WORLDMAP_NORTH | Tile::WORLDMAP_WEST | ... values, 
+   * which indicates the directions Tux can move to when at the given position.
+   */
+  int available_directions_at(Vector pos);
+
+  /**
+   * returns a bitfield representing the union of all Tile::WORLDMAP_XXX values 
+   * of all solid tiles at the given position
+   */
+  int tile_data_at(Vector pos);
+
+  size_t level_count();
+  size_t solved_level_count();
+
+  /**
+   * gets called from the GameSession when a level has been successfully
+   * finished
+   */
+  void finished_level(Level* level);
+
+  LevelTile* at_level();
+  SpecialTile* at_special_tile();
+  SpriteChange* at_sprite_change(const Vector& pos);
+  Teleporter* at_teleporter(const Vector& pos);
+
+  /** Check if it is possible to walk from \a pos into \a direction,
+      if possible, write the new position to \a new_pos */
+  bool path_ok(Direction direction, const Vector& pos, Vector* new_pos);
+
+  /**
+   * Save worldmap state to squirrel state table
+   */
+  void save_state();
+
+  /**
+   * Load worldmap state from squirrel state table
+   */
+  void load_state();
+
+  const std::string& get_title() const
+  { return name; }
+
+  /**
+   * runs a script in the context of the worldmap (and keeps a reference to
+   * the script (so the script gets destroyed when the worldmap is destroyed)
+   */
+  HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+
+  /**
+   * switch to another worldmap.
+   * filename is relative to data root path
+   */
+  void change(const std::string& filename, const std::string& force_spawnpoint="");
+
+  /**
+   * moves Tux to the given spawnpoint
+   */
+  void move_to_spawnpoint(const std::string& spawnpoint);
+
+  /**
+   * returns the width (in tiles) of a worldmap
+   */
+  float get_width() const;
+
+  /**
+   * returns the height (in tiles) of a worldmap
+   */
+  float get_height() const;
+
+private:
+  void get_level_title(LevelTile& level);
+  void draw_status(DrawingContext& context);
+  void calculate_total_stats();
+
+  void load(const std::string& filename);
+  void on_escape_press();
+};
+
+} // namespace WorldMapNS
+
+#endif