"""
vn game engine for Honey Select Studio / NEO / PlayHomeStudio / CharaStudio / AI-Shoujo by Keitaro-kun
"""
vnge_version = "15.0"
"""
Key shorcuts:
- Ctrl+F8 - show/hide main window
- Ctrl+F3 - return to title screen
For developers:
- Ctrl+F4 - toggle developer console
- Ctrl+F1 - reloading and restarting vn game engine
- Ctrl+Alt+F4 - dump camera state (saved in dumppython.txt)
- Ctrl+Alt+F5 - dump scene state (saved in dumppython.txt)
-----
Changelog:
2.0 
- added NEO support, some refactor
2.1 
- for Studio - added change_female_num_animation and change_male_num_animation
3.0 
- added CharaStudio support (Koikatsu game)
- added updFunc param for call on latest update
- due to updFunc feature also added "Loading scene..." feature during scene loading for all engines. See "load_scene" function for example
3.1
- Studio: self.isDevDumpButtons removed, for dump camera use Ctrl+F4, dump scene - Ctrl + F5
- All: set_timer function available. Example in maingameunstablefeatures
- All: functions anim_sim_zoom_in and anim_sim_zoom_out available. Example in maingamedemo
- All: return to main screen using Ctrl+F2
- Studio: anim_to_camera_num and anim_to_camera funcs available. They are for animation move between cameras. 
Latest param is animation style - linear,slow-fast,fast-slow,slow-fast3,fast-slow3,slow-fast4,fast-slow4
- Studio: added high quality demo for manipulating chars - Ruris Dream by Kningets
4.0
- NEO,CharaStudio: dump camera using Ctrl+F4, move_camera func available
- NEO,CharaStudio: anim_to_camera, anim_to_camera_num funcs available
- NEO,CharaStudio: changed to_camera function to more correct code
- NEO,CharaStudio: internal changes - common code moved to VNNeoController
- All: game.isHideWindowDuringCameraAnimation option for hide game window during camera animation
- All: function on end camera movement can be defined
- All: funcs show_blocking_message, show_blocking_message_time, hide_blocking_message available. Used during dump camera or scene
- All: return to main screen switched to Ctrl+F3
- All: params can be passed in Buttons actions - if it is tuple.
Example: game.set_buttons(["Simple examples", "Advanced examples"], [simple_tests, (adv_tests, "Advanced examples as param")])
def adv_tests(game, param):
- Studio: specific code moved to vngameenginestudio.py
- NEO,CharaStudio: manipulating characters functions + dumping scene (Ctrl+F5):
-- save position, rotation, scale for chars
-- save animation state for chars
- NEO,CharaStudio: new advanced cool camera moves:
-- try game.anim_to_camera_num(3, param, {'style':"linear",'zooming_in_target_camera':6}) # cool camera move with zoom-out - zoom-in   
-- try game.anim_to_camera_num(5, 0, {'style':"linear",'add_distance_target_camera':-0.2}) # this is example of slow zoom out - 5 seconds, -0.2 camera distance from 0 - current camera
- All: menus features - see examples in techdemo
- All: use Ctrl+F6 to print list of scene chars in Console
5.0
- NEO,CharaStudio: buttons works with mouse clicking
- All: function to set background to arbitrary PNG-file - use game.scene_set_bg_png(filepng) (filepng must be in all scenes game folder)
- All: unified code for camera animation and targeting
- All: techdemo - new cool options and demos for camera movement
- All: set_buttons_alt function - example:
game.set_buttons_alt([ # set_buttons_alt receive 1 array intermediately placed Text and Functions
    "Text test", toText,
    "Timer test(15 seconds)", toTimer,
    "System message", toSysMsg
])
- All: game autoprocess large number of buttons, splitting it by pages by game.maxBtnsBeforeSeparator items.
By default game.maxBtnsBeforeSeparator = 5, so max 5 buttons on page.
See example in techdemo / Simple examples - how 10 buttons in a row processed.
So, you don't need to manually split buttons/actions by pages - game do it by itself
- All: automaking list of available games
first line of your game must be
#vngame;<engine_name>;<game_name>
Engine names are: studio, neo, charastudio (also "all" in case of your game is engine-independent - like in examples)
As an example:
#vngame;studio;Ruri Adventure
- All: autoreloading game during the next run (after Ctrl+F3) - so, you can change it and reload without reloading Studio
- All: autochoose the correct engine (no switch between Studio/NEO/Chara) using vngame_window_autogames_uni() call in Console.ini
Also, no annoying windows in games itself
- All: support for AutoTranslation of game texts using XUnity.AutoTranslator. So, you can see the game texts on your own language!
Detailed desc in AutoTranslation_desc.txt
- All: some options - keys for buttons, show/hide, reset and auto-translations are located in vngameengine.ini file. Edit it carefully!
6.0
- All: INI can be configured to run games by hotkeys
Hint for developers: game code will be reloaded by hotkey run, so it extremely useful for debugging
- All: INI key shortcut for hot reload of full vngameengine.py
- All: INI option for start at hidden mode
- All: Developer console instead of direct dump calls. Enabled by Ctrl+F4 (config in INI)
- Chara: known BUG: temporary return to_camera function to simulating key presses.
Due to unknown reason we are not always setting camera at correct position by num by classic code.
- All: INI option to disable keyboard shortcuts for buttons
- NEO, CharaStudio: VNSceneScript engine released!!!
VN Scene Script is a framework/script for running stories, written in folder names just in scene files.
So, in simplest cases you don't need PY scripts at all - just scene.png file
- NEO, Chara: added functions to run VNSceneScript scenes as intro with returning to main game
function: vnscenescript_run_filescene(file,onEnd)
see example in techdemo
- CharaStudio: functions to get and set frames for scene
scene_set_framefile and scene_get_framefile (see in techdemo)
- All: optimization: autogames feature scan only .py files
6.5
- Neo,Char: fixed bug in HSNeoOCIChar.animate function
- Neo,Chara: HSNeoOCI has property visible_treenode to manipulate treenode
- also set_parent function
- All: set_timer allows you to pass tuple as onTimerEnd function
- Neo,Chara: HSNeoOCIFolder class available, added to techdemo
- Neo,Chara: lots of properties for HSNeoOCIChar - look eyes etc.
- All: set_buttons(_,_,"compact") for place buttons compact (up to 8) on screen
- Neo,Chara: HSNeoOCIFolder has static functions add, find_all, find_single, find_all_startswith, find_single_startswith
- Neo,Chara: Added dummyneoclasses.py file to get dummy classes for Studio internals.
- Neo,Chara: HSNeoOCIChar contains much new functions
- VNSceneScript updated to 1.1:
-- Added Developer utils - button to add typical header
-- Devutil: save action with current camera in debug mode
-- Devutil: can add dummy texts on default cameras
-- Devutil: sync with file vnscene_sync.txt (you can load commands from external text file, not write it in Studio!)
7.0
!! ready for PlayHome - engine, demos etc.
!! (Main was tagged set of NEO-engines - NEO, PlayHomeStudio, CharaStudio)
- All: error handling for incorrect menu calls
- Main: functions for manipualate backgrounds - see techdemo
- All: param game.isHideGameButtons - for temporarily hiding the buttons
- All: adapted set_timer and clear_timer functions (thanks @countd360)
- All: auto stop camera animation during set new camera animation (thanks @countd360)
- All: added game.onSetTextCallback param - if you want to made some auto-actions during set_text call (needed for framework authors)
- Studio: fixed a bug with camera animation and reset
- Main: game.onDumpSceneOverride param - set it for your own dump scene script
7.0.1
- bug fixed: disabled keyboard buttons during game.isHideGameButtons = true
7.5
- Main: HSNeoOCIItem added to manipulate items
- Main: functions scene_get_all_items, scene_get_all_folders
- Main: functions HSNeoOCIChar.add_female(path), HSNeoOCIItem.add_item(no)
- Main: functions HSNeoOCIFolder - rot, pos set/get
- Main: global functions dump_selected_item_tree() and load_item_tree(obj,parentItemTreeObj) for saving item tree and load it
- Main: dev console - ability to dump selected item tree
- All: define your own custom buttons interface when you need - colorize, labels, etc - all Unity abilities
use game.set_buttons_alt([],("function", custFunctionGUI)) for this - example in techdemo
- Main: HSNeoOCIChar.as_actor property - return Actor from @countd360 framework
- Main: include vnactor.py - Actor class from @countd360 (thanks!)
Actor allows to manipulate chara in different ways. It was developed for CharaStudio, but usable in NEO too.
- All: try to clearing game data after returning to main screen
(Please, save your game information in game.gdata property - it will be cleared automatically)
- Main: integrated Actor and Props framework. New functions available:
scenef_register_actorsprops (for registering actors and properties)
scenef_get_all_props, scenef_get_all_actors, scenef_get_prop, scenef_get_actor
- Main: integrated fake lip sync (thanks @countd360). Example can be found in all demos - it's modified Simple game demo
7.6
- All: call_game_func achieved calls like [func] and ([func] param). This calls don't pass game params to func
- All: key shortcut for restart current game - F10 (INI)
- All: key shortcut for run VNFrame developer console - Ctrl+F5 (INI)
- Main: dump camera bug fixed
- Main: dump scene bug fixed (on non-latin chars) (i hope)
- Main: lip sync moved to vngelipsync,
- Main: lip sync - you can define engine for lip sync! set self.fAutoLipSyncVer = "v10" or self.fAutoLipSyncVer = "v11"
- Main: VNActor updated to 1.1
7.7
- All: get_engine_id() function return name of engine
- VNActor and VNFrame v1.2.1 - more powerful functions, more simple code, script_helper to made an animations
(script_helper mostly support only CharaStudio at now) (great thanks to @countd360)
- VNSceneScript 1.5:
-- Dev: some useful buttons
-- A action: return no more than 3 params. Third param can contain : symbols (needed for vnframe ext)
- VNSceneScript: vnframe10 extension. This extension provide a way to use VNFrame functions inside VNSceneScript!
-- So, you can change almost whatever from VNSceneScript - blush, movement, cloth, juice etc. and also animate some props!
8.0
(sorry, no tutorials for new features, please, wait for next versions)
- Main: dev console button "VNFrame scene dump"
- Main: functions scenef_clean_actorsprops, scenef_get_propf (return Prop)
- All: event system - allows your code "listen" to some events in system
-- functions event_dispatch, event_reg_listener, event_unreg_listener
-- example is in techdemo
-- available system events:
set_text - on set_text call
update - on every update (please, use it wisely, it calls frequently)
before_scene_load (calls only during system load_scene call)
before_scene_unload (calls only during system load_scene or reset calls)
- listeners auto-clean during return to main screen (end game and cleaning functions)
- All: game.scenedata var
Please, use this var for save scene-specific data - links for objects etc.
game.scenedata autocleans during scene unload
- VNFrame and VNActor updated to 1.2.3 - support for NEO and PlayHome, but on limited properties
limitations for NEO, PlayHome: no FK/IK support, no cloth/accessory changes, no juice handling.
almost all other supported (pos, rotate, scale, visible, face and neck states)
aditional limitation for PlayHome: not all face states
VNFrame contains hack for run VNSceneScript_helper
- VNSceneScript 2.0
!! acode construction
- dev utils for new version
- version is saved in game.scenedata.scVer
- internal vars moved to game.scenedata to correct cleaning
!! "nul" action - do nothing
- in text actions - "\n" replaces to \n
!! "txtf" action - show text as third param, can contain ":" symbols;
example :a:1:txtf:s::Some text with : symbol
- flipsync10 extension to start engine fake lip sync
-- VNSceneScript_helper for VNFrame (file vnframe_vnscenescripthelper)
- provides a fast way for making states in Studio interface
- support NEO, PlayHome, CharaStudio
8.1
- VNActor and VNFrame v2.0 - full support for NEO and PlayHome (thanks to @countd360)
(and also a lot of other features in them, waiting for tutorials)
- Mains: changed scenef_register_actorsprops, scenef_get_propf for correct work with Actor 2.0
- HSNeoOCIChar: changed as_actor for correct work with Actor2.0
8.2
- NEO: removed unused reference clr.AddReference('UnityEngine.UI.Translation') from console.ini
- VNFrame docs added (see folder VNFrame docs)
- VNFrame_VNSceneScript: added option "EXCLUDE FK&IK&Voice". Option allow remove this options from full/diff dump (if you want minify it).
- techdemo: added example for using VNActor
- VNFrame and VNActor updated to 2.1
- VNSceneScript 2.1
-- Devutil: sync with files acode, sync
-- Devutil: template for v20
-- Devutil: colorizing
8.5
- Main: added VNFrame Tutorial for all engines! (thanks @countd360)
- All: INI - up to 10 games run using keyboard shortcuts
- Main: sync_h function for syncronizing female and male animation
- VNSceneScript updated to 2.2
-- Devutil: vnframe11
-- Devutil: gagency01
- VNFrame and VNActor updated to 2.2
- Chara: finally included Laboratory Girl high-quality demo (thanks @countd360)
8.6
- All: gpersdata set of functions. Allows you to auto save/load game data.
gpersdata_load(), gpersdata_save(), gpersdata_set(param,val), gpersdata_get(param)
Examples are in techdemo / Simple Examples
- All: checkpoints set of functions. Allows you to make galleries, game checkpoints etc.
Examples in All Demos / Main Demo with checkpoints
- VNActor and VNFrame updated to 2.3
- Mains: fixed small bug in scenef_register_actorsprops()
8.7
- VNActor and VNFrame updated to 2.3.2
# - allow call "game_func" to run game functions
# example: {'game_func': [(sup_print, "1"), (sup_print, "2")]} or {'game_func': (sup_print, "test1")}
- dump_camera - added , to vnframe code
- folders in prepare_auto_games() list (thanks to @Kningets)
Examples: #vngame;neo;Demos/Subdemos/Demo game 1, #vngame;neo;Demos/Demo2
Also rules: please, use from now folder Demos for demos, Utilities for utilities.
- rule for lip sync - if text started with "!", no fake lip sync starts (see simplegamedemoadv2 for examples)
- vnlibfaceexpressions.py - face expressions lib. Allows you to change face in standart script.
example of use: game.texts_next([
    ["me//angry_whatyousay", "What are YOU say??"],
]...)
examples in simplegamedemoadv2 (for NEO - simple game demo - lip sync in All demos)
8.9 (9.0 beta)
- All: skin system!!
-- there are three skin in package: skin_default, skin_renpy, skin_customwindow
-- for VN skin_renpy is best (see Simple game demo)
-- for utils like ScriptHelper you can use skin_customwindow
-- you can dev your own skins, see skin_renpy or skin_customwindow as reference
- Main: VNActor and VNFrame 2.3.5 - updated for FK items and skin system (thanks to @countd360)
- Main: Dev console has VNActor select dump allows you to dump state of selected chara in tree (even without id)
- Main: libfaceexpressions 1.1 - added lot of NEO male faces (thanks to @cochese42)
- All: markupreplacer lib - some text processing if you want (thanks to @cochese42)
- Main: vnscenescript - you can set default skin for your VNSceneScript (INI)
8.10 (9.0 beta 2)
- Main: HSNeoOCIProp class. This allows you to convert object to correct Prop class from VNActor.
- Main: global params HSNeoOCIChar.globalActorClass and HSNeoOCIProp.globalPropClass
this uses for overriding actor and prop classes due to concrete using
(for example, if you detect a plugin and want to share additional properties)
it's not good to use them, but it's necessary for some cases
- VNFrame 2.3.8 - allows you to pass object as 'act' function params.
9.0
- All: show version, start text adjust to it
- All: Console window hidden at startup, to see it change showAtStartup=1 in Console.ini
- Mains: integrated Simple Novel Manager 1.1 - now available from main screen
- VNActor 2.3.9
-- contains import_status for Prop and Actor
- skin_renpy 1.1 - reduced window height, set up max buttons
- All: skin system allows you set max number of buttons in params
- All: simplegamedemo uses skin_renpy by default
9.1
- Mains: Added HSNeoOCILight, HSNeoOCI now supports OCILight (thanks to @chickenman)
- VNFrame 2.3.10 - chickenman, Keitaro (thanks to @chickenman)
# - added support for OCILight as Prop
# - import_status_optimized_diff for Actor
- VNFrame (register_autoprops) supports -propchild: property (needed for Light handling)
- extplugins.py 1.1 added - for extended plugins handling (thanks to @chickenman)
9.1.1
- All: fixed bug in CustomGUI calls in skins: default and renpy. Now "game" param is sent as first param.
10.0
- CharaStudio, PlayHome: UTF-8 support! (some libraries added to X_Data/Managed)
- All: functions file_get_firstline_utf8, file_get_content_utf8
- Main: VNFrame 2.4 - (thanks to @countd360)
# vnactor:
# - support IK&FK mode, need extplugins.py.
# to use IK&FK, need HSStudioNEOAddon.dll for NEO, PHSAddon.dll for Playhome.
# - Support for light extends to Playhome and KoiKatsu.
# Though the light support is not good in playhome and koikatsu, these function still work.
# (To add a light in playhome you need PHIBL.dll. And light in koikatsu can not effect character.)
# vnframe:
# - support UTF-8! 
# - [Create TAG] button in ScriptHelper can create -propchild: tag for lights now.
- PlayHome: extplugins support PHSAddon.dll (thanks to @countd360) 
- Main: Simple Novel Manager v1.2
# - UTF-8 support in config and text files
# - support "skin" param in config. Example: "skin": "skin_renpy"
- Main: VN Scene Script v2.3 - UTF-8 support during sync with text files
- All: added libjsoncoder lib (thanks to @chickenman)
- HSNeoOCILight: fixed bug with pos and rot properties
11.0
- All: Ctrl+F3 doesn't reset current scene - so, back to menu freely )
- extplugins: 
-- NEO: support HSStudioNEOExtSave, 
-- CharaStudio: pre-support KKPE
- Main: VNFrame 3.0 (3.1.3)! (by @countd360)
vnactor 3.0:
- import_status_diff_optimized() function for Prop
vnframe 3.0:
- A new feature: "key frame clips" added!
- Create and manage key frame based animation clips in Script Helper!
- A clip can animate tagged actors and props, camera and system, and sync which another clip.
- Several clips can be run in the same time. So you can create one clip for one actor, or
even several clips for one actor, and then run them at same time to make an animation.
- Clip can be controlled in act script like a game build-in anime. 
Clip status can be dumped by script helper.
- In script builder can choose which actor/prop/clip you want to dump now.
- Auto script template update:
- Support key frame clips
- Support skin selection
- Choice to show a "restart" button the end of game
- Choice to use quick reload, which restart game without reload the scene file.
- (act function) call import_status_diff_optimized for Prop and Actor instead of direct processing.
- [Create Tag] button in scene helper will register the new tagged object to actor/prop automatically.
vnanime 1.0:
- The new key frame animation clip engine.
Also:
- vnframe 3.1.3 - add IK&FK mode for KK 
-- NEO, PH, CharaStudio: VN Frame Tutorial 2 (in Demos section) to understand new features!
11.5
- vngame_window_autogames_uni_1init(), studio_wait_for_load() - for solve problem with loading timeout on start
- changed Console.ini to adequate load on start
# extplugins 2.2 by @countd360
# - NEO: HSStudioNEOAddon - Add some function to support play MMD
# - KK: KKVMDPlayPlugin - a plugin to support play MMD
# vnactor v3.2 (beta) (Keitaro)
# - Support for add data from external plugins to export_full using options from vnactor.ini
# - NEO: support save/load HSNeoExtSave (HSStudioNEOAddon.dll) plugin data in "sys"
# vnactor v3.2.1 (Keitaro)
# - Error wrapping in face_to_full call
11.6
- warning if you have problems with UTF-8
- added testutf8.py file
- small changes in vnframe v3.2.1 - removed UTF-8 header to increase compatibility
11.7
- fix some bugs in startup - removed incorrect CameraControl import, added debug info in vngame_window_autogames_uni
- vnanime 1.1.3 - removed utf-8 dependence
- extplugins 2.3 - HSPENeo plugin support
- vnactor 3.3 - HSPENeo plugin support
(to save actor's HSPE data during making VN or using SSS set ExportChara_HSPENeo=1 in vnactor.ini)
11.7.1
- CharaStudio, PH: fixed bug with loading - set console ShowAtStartup=1
11.8
- new demo - demovnscenescriptbased.py - how to make a game based on VNSceneScript files
!! vnframe v3.2.2 (Keitaro) - fix for correct work with VNSceneScript
# - fix for using act function without game.gdata.kfaManagedClips
- pre-support of KKPE (works on internal version, don't use it for now):
extplugins 2.4, vnactor 3.4
12.0
- VNActor v3.5 (Keitaro)
# - support functions - bytearray_to_str64 and str64_to_bytearray
# - CharaStudio: save neck params when position Fixed - 'face_to_full2' param (optimized version to use str64)
# - NEO: save neck params to str when position Fixed - 'face_to_full2' param (optimized version to use str64)
# - CharaStudio: ready for KKPE 1.2.0+, use ExportChara_KKPE=1 in vnactor.ini
# - CharaStudio: error handling for FK set
# - CharaStudio, NEO: export cur clothes, use ExportChara_CurClothesCoord=1 in vnactor.ini (for NEO may be buggy)
- extplugins 2.5 
# - CharaStudio: Screencap plugin support
12.5
- All: HSNeoOCI.create_from return None if None as input. Useful for create_from_selected error handling.
- All: fix bug in random_randint and random_choice functions
- All: VNScenePlayer (F8) use renpy skin. You can change it back by INI param VNSceneScriptRunSkin=skin_default
- VNSceneScript 2.5:
-- show custom buttons in compact way
-- support scene params "end" in nextstate and addbtn calls
-- optimized blank states processing - no recursion, while cycle now
-- added emergency out if state is over 1000000
-- "addbtnrnd" action to move to state randomly. Syntax: addbtnrnd:<btntext>:<states separated by ",">  Example: addbtnrnd:Random action:200,300,400 
-- v25 by default - changed menu items to handle it
- VN Scene Script Ext - vnframe12
-- added function f_actm_j for call with JSON serialized str
- extplugins 2.6 (VNGE 12.5)
-- All: has_plugin support function added
- libjsoncoder 1.1
-- added wrapper for code/encode
12.6
- All: timers increased to 8
- Dump VNFrame also dump sys and cam
- vnscenescript call can be run from arbit state
- VNSceneScript 2.6: allow to run from arbitrary state
13.0
- processing FOV
- processing FOV while animating cam
vnscenescript 3.0
- timernext support syntax: timernext:<time>:<nextstate>
- new commands: showui, hideui, hideui:<timerforhide>
- Ministate button run cmd - addbtnms:<text>:<ministatename>
- upgrade to run v30
- Adv cam function - camoanim3:<camstr>:<duration>:<style>:<effZoomOut>:<effRotX>:<effRotZ> (use 0.0 on effects if you don't want effects)
- Ministate immediately run cmd - runms:<ministatename>
- game is not hide UI during animation by default - you can do it manually in wizard
- support scene params "next" in addbtn calls
- commands for control buttons in UI - lockui/lockui:timeout/unlockui
vnanime 1.2
- When you click 'Export Clips' button in script helper, you can choice export to scene or to txt/py file
- Auto and manual load in-scene clips.
- Stop update clip after last keyframe played. So if the last keyframe is not the last frame, clip will not lock to last keyframe state any more when we passed it.
vnanime 1.3:
- A new clip optimization UI, provide more save and controllable optimize.
- Clips sorted by priority first and then by clip's name, more easy to find.
- Status list of actor/prop/system can be sorted/translated/colored by setting in vnactor.ini, more easy to understand and handle.
- Adjust of curve data applied automatically, no 'update' button click needed.
vnactor 3.6:
- A little fix that match char_anime()'s param with vnactor.animate(). So you can pass the fifth param as 'force reload' to char_anime() now.
- fix light color support for CharaStudio
- load status translate strings for vnactor.ini, (support function for vnanime update)
a updated vnactor.ini included, merge the [translation] section to your owns.
vnframe 3.2.3:
- add a "reset" button in scene helper tab to reset vnanime (maybe temporary, not necessary in most case...)
vnscenescriptext_vnanime10.py
An new ext for vnscenescript to support vnanime clips.
Use f_clipinit to init clip manager and load in-scene clips.
Use f_clipplay, f_clippause, f_clipstop, f_clipseek to control the play back of clips.
Read comment in vnscenescriptext_vnanime10.py for detail.
vnscenescriptext_flipsync10.py
- Add a "read speed" param to initflipsync function, so you can set reading speed now.
libministates.py
- lib for run and manage ministates
skin_renpy.py
- optional additional button in top-right corner

13.1
- NEO: vnanime patched for correct use with VideoExport plugin (thanks Joan)
# vnactor v3.6.1 (Keitaro)
# - CharaStudio: export normalizedTime if anime speed == 0.0
# Allow save characters in middle of animation

13.9 (beta)
- support for AI-Shoujo (Neov2)
- changes in some file - removed "box1" incorrect call. Increased performance
- VNActor 3.7 - support for AI-Shoujo
13.9.1 (beta)
- AI-Shoujo - fix to correct use VNFrame console (added scene_get_framefile function)
- AI-Shoujo - fixed loading for patch3110.
14.0
- AI-Shoujo, CharaStudio: autocalc PY path for correct work with Bep5 and Bep4 anyway
- option HideConsoleAfterStart=1 - to hide console after start
- VNActor 3.7.1 (by countd360) - fixes for AIShoujo
- AIShoujo - AIPE handle
- AIShoujo - allow save clothes (using vnactor.ini param)
- VNActor 3.7.2 (by Keitaro) - support for save shoes (in/outdoor) in actor states (CharaStudio).
14.1
- Extplugins 2.8 - correct check additional plugins in Bepinex4 and Bepinex5 installation
- VNActor 3.8 - support for saving Body Shapes (CharaStudio, NEO, AIShoujo)
- small fix to start even if no HideConsoleAfterStart prop in INI file
15.0
- Skin "skin_renpymini" - even mini skin for VN. Now default to VNScenes!
- ..some other small bugfixes
VNActor v3.10 (Countd360)
- support for body and face shapes (all engines)
- support char light (or camera light) in system export (all engines)
- support export anime aux param, set it in ini file (all engines)
- new property "animeLength", to get length of actor's anime (all engines)
- add some function to support change export setting in runtime
- fix skin wet import bug (AIShoujo)
- support for Nodes Constraints plugin (Neo,KK) (Keitaro)
VNAnime v1.4 (countd360)
- fix anime speed save bug
- fix seek bar time display bug
- work with vnactor v3.9
Extplugins 2.9
- Support for NodeConstraints plugin
 
---------
For cool developers:
- main game classes are located in vngameengine.py

"game" is an instance of one of classes, depending on engine, where we run game

There are several classes:
- BaseController - just for window instantion
- VNController(BaseController) - for main vn game engine. Contains functions for buttons, timers, main window, etc.
- StudioController(VNController) - controller for Studio. Contains scene loading, move between chars etc. - placed in vngameenginestudio.py
- VNNeoController(VNController) - base controller for all NEO-based engines - line NEO, PlayHome Studio (perspectively), CharaStudio in Koikatsu. Contains move between cameras, animation between them and so on.
- NeoController(VNNeoController) - controller for NEO. Contains specific scene loading.
- CharaStudioController(VNNeoController) - controller for CharaStudio. Contains specific scene loading.
- PHStudioController(VNNeoController) - controller for PlayHomeStudio. placed in vngameenginephstudio.py

All examples usually can be found at this places:
- maingamedemo.py
- simplegamedemoadv.py
- techdemo.py
"""
def importOrReload(module_name): return import_or_reload(module_name)

def import_or_reload(module_name):
    import sys

    if module_name in sys.modules:
        reload(sys.modules[module_name])
    else:
        __import__(module_name)

    if module_name in sys.modules:
        return sys.modules[module_name]
    return None


def get_engine_id():
    from UnityEngine import Application
    dpath = Application.dataPath
    ar = dpath.split("/")
    gameId = ar[len(ar)-1]
    if (gameId == "HoneyStudio_32_Data") or (gameId == "HoneyStudio_64_Data"):
        return "studio"
    if (gameId == "StudioNEO_32_Data") or (gameId == "StudioNEO_64_Data"):
        return "neo"
    if (gameId == "PlayHomeStudio32bit_Data") or (gameId == "PlayHomeStudio64bit_Data"):
        return "phstudio"
    if (gameId == "CharaStudio_Data"):
        return "charastudio"
    if (gameId == "StudioNEOV2_Data"):
        return "neov2"
    return ""

def vngame_window_autogames_uni():
    global vnge_version
    from UnityEngine import Application
    dpath = Application.dataPath
    ar = dpath.split("/")
    gameId = ar[len(ar)-1]
    if (gameId == "HoneyStudio_32_Data") or (gameId == "HoneyStudio_64_Data"):
        print "VNGE", vnge_version, "starting..."
        vngame_window_studio(["autogames"], [])
        print "VNGE", vnge_version, "inited!"
        return True
    if (gameId == "StudioNEO_32_Data") or (gameId == "StudioNEO_64_Data"):
        print "VNGE",vnge_version,"starting..."
        vngame_window_neo(["autogames"], [])
        print "VNGE", vnge_version, "inited!"
        return True
    if (gameId == "PlayHomeStudio32bit_Data") or (gameId == "PlayHomeStudio64bit_Data"):
        print "VNGE", vnge_version, "starting..."
        vngame_window_phstudio(["autogames"], [])
        print "VNGE", vnge_version, "inited!"
        return True
    if (gameId == "CharaStudio_Data"):
        print "VNGE", vnge_version, "starting..."
        vngame_window_charastudio(["autogames"], [])
        print "VNGE", vnge_version, "inited!"
        return True
    if (gameId == "StudioNEOV2_Data"):
        print "VNGE", vnge_version, "starting..."
        vngame_window_neov2(["autogames"], [])
        print "VNGE", vnge_version, "inited!"
        return True
    print("VN Game engine is not for this EXE file")
    return False

inited = False
def vngame_window_autogames_uni_1init():
    global inited
    if not inited:
        inited = True
        vngame_window_autogames_uni()

def parseKeyCode(s):
    from System import Enum
    from UnityEngine import KeyCode
    try:
        if s:
            ctrl, alt, shift = False, False, False
            keys = s.lower().split('+')
            shift = 'shift' in keys
            ctrl = 'ctrl' in keys or 'control' in keys
            alt = 'alt' in keys or 'meta' in keys
            code = Enum.Parse(KeyCode, keys[-1:][0], True)
            return (s, code, ctrl, alt, shift)
    except:
        pass
    return (s, KeyCode.None, False, False, False)


def reloadKeyCodes():
    global _keycodes
    _keycodes = {}


def parseIniFile():
    global _keycodes
    global _engineoptions
    try:
        _keycodes
    except NameError:
        _keycodes = {}
    try:
        _engineoptions
    except NameError:
        _engineoptions = {}
    try:
        import ConfigParser, sys, os.path
        config = ConfigParser.SafeConfigParser()
        config.read(os.path.splitext(__file__)[0] + '.ini')
        for k, v in config.items('Shortcuts'):
            _keycodes[k.lower()] = parseKeyCode(v)
        for k, v in config.items('Options'):
            _engineoptions[k.lower()] = v
    except Exception, e:
        print("Error: " + str(e))
    if not _keycodes.get('hide', None):
        _keycodes['hide'] = ('Ctrl+F8', KeyCode.F8, True, False, False)

def getKeyCodes():
    global _keycodes
    return _keycodes

def checkKeyCode(iniparam):
    global _keycodes
    try: # disabling due to performance issues
        iniparam = iniparam.lower()
        import unity_util
        from UnityEngine import Input, KeyCode
        if iniparam in _keycodes:
            (_, icode, ictrl, ialt, ishift) = _keycodes[iniparam]
            if Input.GetKeyDown(icode):
                # unity sucks for checking meta keys
                ctrl, alt, shift = unity_util.metakey_state()
                if ctrl == ictrl and alt == ialt and shift == ishift:
                    return True
        else:
            return False
    except Exception, e:
        pass
    return False

def getEngineOptions():
    global _engineoptions
    return _engineoptions

def translateText(value):
    if not value: return value

    global _translateHookChanged
    try:
        _translateHookChanged
    except NameError:
        _translateHookChanged = None
    if not _translateHookChanged:
        try:
            import clr
            clr.AddReference('XUnity.AutoTranslator.Plugin.Core')
            from XUnity.AutoTranslator.Plugin.Core import AutoTranslationPlugin
            _translateHookChanged = AutoTranslationPlugin.Current.Hook_TextChanged
            print "AutoTranslator enabled!!"
        except Exception, e:
            _translateHookChanged = lambda x: x
            print "AutoTranslator disabled or not found"
            #print("Error in AutoTranslator: " + str(e))
    from UnityEngine import GUIContent
    if isinstance(value, GUIContent):
        if not value.text: return value
        _translateHookChanged(value)
        return value
    else:
        content = GUIContent(str(value))
        _translateHookChanged(content)
        return content.text

class GData():
    def __init__(self):
        pass

class BaseController():
    def __init__(self):
        from UnityEngine import Screen, Rect
        
        self.gameObject = None # will be assigned if exists as member
        self.component = None # will be assigned if exists as member
        self.counter = 1
        self.show_buttons = False
        self.visible = True
        self.style = None
        self.cameraControl = None
        self.windowRect = Rect (Screen.width / 2 - 50, Screen.height / 2 - 50, 400, 400)
        self.lastCameraState = True
        self.gameCursor = None
        self.windowName = "Window"
        # loading options
        parseIniFile()
        self.engineOptions = getEngineOptions()
        #print self.engineOptions

    def EnableCamera(self, value):
        if self.lastCameraState != value:
            self.lastCameraState = value
            if self.cameraControl:
                self.cameraControl.enabled = value
            self.gameCursor.enabled = value
        
    def ResetWindow(self, windowid):
        from UnityEngine import GUI, GUIUtility
        from UnityEngine import Event, EventType
        #Workaround mouse/camera issues when dragging window
        if GUIUtility.hotControl == 0:
            self.EnableCamera(True)
        if Event.current.type == EventType.MouseDown:
            GUI.FocusControl("")
            GUI.FocusWindow(windowid)
            self.EnableCamera(False)
        
    def Start(self):
        try:
            import UnityEngine
            import GameCursor, CameraControl
            from UnityEngine import GameObject



            #self.useGUILayout = True

            if self.isClassicStudio:
                if self.cameraControl == None:
                    self.cameraControl = GameObject.Find('StudioCamera/Main Camera').GetComponent[CameraControl]()
            #if GameObject.Find("EventSystem") == None:
            #self.Instantiate(self.cameraControl);
            if self.gameCursor == None:
                self.gameCursor = UnityEngine.Object.FindObjectOfType[GameCursor]()
        except Exception, e:
            print "VNGE: passable error in Start: ", e
            pass
        
    def OnGUI(self):
        from UnityEngine import GUI
        if not self.visible: return
        self.windowRect = GUI.Window(0, self.windowRect, self.windowCallback, self.windowName)
    
    def Update(self):
        import unity_util
        from UnityEngine import Input, KeyCode
        # Update is called less so better place to check keystate

        if checkKeyCode('hide'):
            self.visible = not self.visible
        """
        if Input.GetKeyDown(KeyCode.F8):
            # unity sucks for checking meta keys
            ctrl, alt, shift = unity_util.metakey_state()
            if ctrl and not alt and not shift:
                self.visible = not self.visible
        """


class SkinBase:
    def __init__(self):
        self.isCustomFuncWindowGUI = False

        self.maxButtonsNormal = 5
        self.maxButtonsCompact = 8
        pass

    def setup(self,controller):
        """:type controller:VNController"""
        pass

    def customWindowGUI(self, windowid):
        pass

    def render_system(self,sys_text):
        pass

    def render_main(self,text_author,text,btnsTexts,btnsActions,btnStyle):
        pass

    def render_dev_console(self):
        print "Skin Dev Console not implemented"
                
import UnityEngine
#import GameCursor, CameraControl
from UnityEngine import GUI, GUILayout, GUIStyle, GUIContent, GUIUtility, Screen, Rect, Vector3, Input, KeyCode
from UnityEngine import Event, EventType, WaitForSeconds, GameObject, Mathf, Time
import System  
from WindowsInput import InputSimulator
from WindowsInput import VirtualKeyCode 
import unity_util             
class VNController(BaseController):
    def __init__(self):
        BaseController.__init__(self)
        
        self.arKeyKodes = getEngineOptions()['keysforbuttons'].split(',')
        self.vnButtonsStyle = "normal"

        self.visible = self.engineOptions["starthidden"] == "0"


        # self.wwidth = 500
        # self.wheight = 230
        #
        # self.windowName = ''
        # self.windowRect = Rect (Screen.width / 2 - self.wwidth / 2, Screen.height - self.wheight - 10, self.wwidth, self.wheight)
        #self.skin_panel_unity = CloneSkin(GUI.)
        self.windowStyle = None
        self.windowStyleDefault = GUIStyle("window")


        self.skin_set_byname("skin_default")
        self.skin_default = self.skin

        global vnge_version
        self._vnText = "Welcome to <b>VN Game engine "+vnge_version+"</b>!\n"

        try:
            import testutf8
            self._vnText = self._vnText
            self._vnText += "\n"
        except Exception, e:
            self._vnText += "<color=red>Warning!</color> You have a problems with UTF-8 libs! See website.\n"
            print "VNGE: problems with UTF-8 libs detected"

        self._vnText += "- "+getKeyCodes()['hide'][0]+" to show/hide this window\n" \
                       "- "+getKeyCodes()['reset'][0]+" to return to this main screen (more in INI file)"
        #self._vnButtons = ["Start >"]
        #self._vnButtonsActions = [self.StartAct]
        
        self.registeredChars = {}
        self.register_char("s", "ff5555", "")
        self.curCharText = "s"
        self.curCharFull = "s"
        
        self.nextTexts = [[]]
        

        

        self.updFunc = None
        self.updFuncParam = ""
        
        self.timers = [-1,-1,-1,-1,-1,-1,-1,-1]
        self.timersFuncUpd = [None,None,None,None,None,None,None,None]
        self.timersFuncEnd = [None,None,None,None,None,None,None,None]
        self.timersDuration = [0,0,0,0,0,0,0,0]

        # preprocessing start options
        if self._vnButtons[0] == "autogames":
            #self._vnButtons = []
            #self._vnButtonsActions = []
            #self.prepare_auto_games()
            #self._vnButtons = ["All games list >>", "(hide this window)"]
            self._vnButtons = ["All games list >>", "Simple novels list >>", "(hide this window)"]
            self._vnButtonsActions = [self.prepare_auto_games, (self.game_start_fromfile, "simplenovelmanager") , self._sup_hide_window]


        self._vnStButtons = self._vnButtons
        self._vnStButtonsActions = self._vnButtonsActions
        self._vnStText = self._vnText

        self.maxBtnsBeforeSeparator = 5
        self._btnSepCounter = 0
        self._arBtnsTextFull = []
        self._arBtnsActsFull = []

        self.gdata = GData()
        self.scenedata = GData()
        self.gpersdata = {}

        self._scenef_actors = {}  # for actors
        self._scenef_props = {}  # for props

        self.current_game = ""

        self._eventListenerDic = {}

        self.isfAutoLipSync = False
        self.fAutoLipSyncVer = "v10"

        self.init_start_params()




        # autoloading feature

        self.windowCallback = GUI.WindowFunction(self.FuncWindowGUI)

    def init_start_params(self):
        self.isShowDevConsole = False

        # menu
        self._menuStack = []
        self.menu_result = None

        self.isHideGameButtons = False
        self.onSetTextCallback = None
        self.camAnimeTID = -1  # timer id for camera animation
        self.onDumpSceneOverride = None

        self.isHideWindowDuringCameraAnimation = False

        self.isFuncLocked = False
        self.funcLockedText = "SYSTEM: Unknown default lock"

        # some settings - may be localized
        self.btnNextText = "Next >"
        #self.autostart = False
        #self.isDevDumpButtons = False - no use
        self.sceneDir = ""

        self.gdata = GData()
        self.gpersdata = {}
        self.scenedata = GData()

        self._scenef_actors = {}  # for actors
        self._scenef_props = {}  # for props

        self.current_game = ""

        # lip sync
        self.isfAutoLipSync = False
        self.fAutoLipSyncVer = "v10"

        self.readingChar = None
        self.readingSpeed = 12.0
        self.readingProgress = 0
        self.lipAnimeTID = -1  # timer id for lip sync animation

        self._eventListenerDic = {}

        self.windowStyle = self.windowStyleDefault
        self.skin_set(self.skin_default)


    def _sup_hide_window(self,game):
        self.hide_window()
    
    def FuncWindowGUI(self,windowid):
        if self.isClassicStudio:
            #self.ResetWindow(windowid) #required for dragging - not work in NEO!!!
            pass

        if self.skin.isCustomFuncWindowGUI: # skin has it's own WindowGUI func
            self.skin.customWindowGUI(windowid)
            return

        if not self.isFuncLocked:
            if not self.isShowDevConsole:
                try:
                    self.skin.render_main(self.curCharFull,self.vnText,self.vnButtons,self._vnButtonsActions,self.vnButtonsStyle)
                except Exception, e:
                    print "Error in skin.render_main, ", str(e)

            else: # show dev console
                try:
                    self.skin.render_dev_console()
                except Exception, e:
                    print "Error in skin.render_dev_console, ", str(e)
        else: # render system message
            self.skin.render_system(self.funcLockedText)




    def dump_camera(self):
        self.show_blocking_message_time("ERROR: Not implemented")

    def dump_scene(self):
        self.show_blocking_message_time("ERROR: Not implemented")

    def dump_scene_vnframe(self):
        self.show_blocking_message_time("ERROR: Not implemented")

    def Start(self):
        BaseController.Start(self)
        
    def OnGUI(self):
        #r = self.windowRect
        #self.windowRect = Rect(r.x, r.y, r.width, 70 if self.show_buttons else 50)

        #if self.windowStyle:
        #    GUI.skin.window = self.windowStyle
        #BaseController.OnGUI(self)
        from UnityEngine import GUI
        if not self.visible: return
        self.windowRect = GUI.Window(0, self.windowRect, self.windowCallback, self.windowName, self.windowStyle)

    def Update(self):
        BaseController.Update(self)
        if self.updFunc != None:
            func = self.updFunc
            self.updFunc = None
            func(self.updFuncParam)
        for i in range(len(self.timers)):
            if(self.timers[i] > 0):
                self.timers[i] -= Time.deltaTime
                if self.timersFuncUpd[i] != None:
                    self.timersFuncUpd[i](self, Time.deltaTime, self.timersDuration[i] - self.timers[i], self.timersDuration[i])
                if self.timers[i] <= 0 and self.timersFuncEnd[i] != None:
                    self.call_game_func(self.timersFuncEnd[i])
        self.UpdateKeyChecks()
        self.event_dispatch("update", None)

    def UpdateKeyChecks(self):

        if checkKeyCode('reset'):
            self.return_to_start_screen_clear()
        if checkKeyCode('ReloadCurrentGame'):
            if self.current_game != "":
                self.game_start_fromfile(self,self.current_game)
        if checkKeyCode('VNFrameDeveloperConsole'):
            try:
                import vnframe
                vnframe.toggle_devconsole(self)
            except Exception, e:
                self.show_blocking_message_time("Error: can't start VNFrame developer console: %s"%(str(e)))

        if self.get_ini_option("usekeysforbuttons") == "1":
            if self.visible and not self.isFuncLocked and not self.isHideGameButtons:
                for i in range(len(self.vnButtons)):
                    if Input.GetKeyDown(self.arKeyKodes[i]):
                        #self._vnButtonsActions[i](self)
                        self.call_game_func(self._vnButtonsActions[i])
        # running games from INI
        self._util_upd_check_and_start_game("game1")
        self._util_upd_check_and_start_game("game2")
        self._util_upd_check_and_start_game("game3")
        self._util_upd_check_and_start_game("game4")
        self._util_upd_check_and_start_game("game5")
        self._util_upd_check_and_start_game("game6")
        self._util_upd_check_and_start_game("game7")
        self._util_upd_check_and_start_game("game8")
        self._util_upd_check_and_start_game("game9")
        self._util_upd_check_and_start_game("game10")
        if checkKeyCode('developerconsole'):
            if self.isShowDevConsole:
                # restoring old window
                self.isShowDevConsole = False
                self.skin_set(self.skin_saved)
            else:
                # set default skin and set console flag to show
                # console must be rendered only in default skin
                self.skin_saved = self.skin
                self.skin_set(self.skin_default)
                self.isShowDevConsole = True

        if checkKeyCode('dumpcamera'):
            self.dump_camera()
        if checkKeyCode('dumpscene'):
            self.dump_scene()


        if checkKeyCode('reloadvnengine'):
            # reload engine
            print "Try reloading engine..."
            try:
                import sys
                reload(sys.modules['vngameengine'])
                sys.modules['vngameengine'].vngame_window_autogames_uni()
                print "Reloading engine success!"
            except Exception, e:
                print("Error in reloading game engine")

    def _util_upd_check_and_start_game(self, gamekey):
        if gamekey in self.engineOptions:
            if self.engineOptions[gamekey] != "":
                if checkKeyCode(gamekey):
                    self.game_start_fromfile(self,self.engineOptions[gamekey])

    def get_ini_option(self,option):
        option = option.lower()
        if option in self.engineOptions:
            return self.engineOptions[option]
        return None

    def return_to_start_screen_clear(self):
        self.clear_timers()
        #self.reset() # before init_start_params to call before_scene_unload event
        self._unload_scene_before() # simulate unloading scene to finish all unneeded features
        # no resetting scene!
        self.init_start_params()
        self.return_to_start_screen()


    def set_timer(self, duration, timerFuncEnd, timerFuncUpd = None):
        # print "Start set_timer!"
        for i in range(len(self.timers)):
            if self.timers[i] <= 0:
                # print "Found timer!"
                self.timers[i] = duration
                self.timersDuration[i] = duration
                self.timersFuncEnd[i] = timerFuncEnd
                self.timersFuncUpd[i] = timerFuncUpd
                # print "New Timer ID =", str(i)
                return i
        return -1  # no more timer valid

    def clear_timer(self, index, runEndFunc=False):
        if runEndFunc and self.timersFuncEnd[index] != None:
            self.timersFuncEnd[index](self)
        self.timers[index] = -1
        self.timersDuration[index] = 0
        self.timersFuncEnd[index] = None
        self.timersFuncUpd[index] = None

    def return_to_start_screen(self):
        self.skin_set_byname("skin_default")
        self.set_text("s", self._vnStText)
        self.set_buttons(self._vnStButtons, self._vnStButtonsActions)
    
    def clear_timers(self): # not calling end function
        for i in range(len(self.timers)):
            if self.timers[i] >= 0:
                self.timers[i] = -1
                self.timersFuncEnd[i] = None
    
    @property
    def vnText(self):
        return self._vnText
    
    @vnText.setter
    def vnText(self,value):
        self._vnText = value
        #self.OnGUI(self)
    
    @property
    def vnButtons(self):
        return self._vnButtons
    
    @vnButtons.setter
    def vnButtons(self,value):
        self._vnButtons = value
        #self.OnGUI(self)
    
    # ---- external game functions ---------
    def set_buttons(self, arButTexts, arButActions, style = "normal"):
        self.vnButtonsStyle = style

        if style == "normal":
            self.maxBtnsBeforeSeparator = self.skin.maxButtonsNormal
        if style == "compact":
            self.maxBtnsBeforeSeparator = self.skin.maxButtonsCompact

        if len(arButTexts) <= self.maxBtnsBeforeSeparator:
            # normal case, not so much btns
            self._btnSepCounter = 0
            self.vnButtons = arButTexts
            self._vnButtonsActions = arButActions
        else:
            self._btnSepCounter = 0
            self._arBtnsTextFull = arButTexts
            self._arBtnsActsFull = arButActions
            self._btnCallSepCounter(self,0)
        #self.OnGUI(self)

    def _btnCallFull(self,game,param):
        self.call_game_func(self._arBtnsActsFull[param])

    def _btnCallSepCounter(self,game,param):
        # wrapping over list
        if param > len(self._arBtnsTextFull)-1:
            param = 0
        # get sublist
        endindex = param+self.maxBtnsBeforeSeparator - 1
        if endindex > len(self._arBtnsTextFull):
            endindex = len(self._arBtnsTextFull)
        ar1 = self._arBtnsTextFull[param:endindex]
        #print param
        #print endindex
        #print ar1

        ar2 = []
        for i in range(len(ar1)):
            ar2.append((self._btnCallFull, param+i))
        # add button to move forward
        ar1.append(">>")
        ar2.append((self._btnCallSepCounter,param+self.maxBtnsBeforeSeparator-1))
        # setting buttons
        self.set_buttons(ar1, ar2, self.vnButtonsStyle)

    def set_buttons_alt(self, arButTextsActions, style = "normal"):
        ar1 = []
        ar2 = []
        for i in range(len(arButTextsActions)):
            if (i % 2) == 0:
                ar1.append(arButTextsActions[i])
            else:
                ar2.append(arButTextsActions[i])
        self.set_buttons(ar1,ar2,style)

    def set_buttons_end_game(self):
        self.set_buttons(["End Game & Return >>"], [self._onEndGame])

    def _onEndGame(self,game):
        self.return_to_start_screen_clear()
    """
    def add_buttons(self, arButTexts, arButActions):
        a1 = self.vnButtons
        a2 = self.vnButtonsActions
        a1 = a1+arButTexts
        a2 = a2+arButActions
        self.set_buttons(a1,a2)
    """

    def set_text(self, char, text):
        char0 = char.split("//")[0]

        self.curCharText = char0
        self.curCharFull = char
        if text.startswith("!"):
            self.vnText = text[1:]
        else:
            self.vnText = text
        #self.OnGUI(self)
        if self.onSetTextCallback != None:
            self.onSetTextCallback(self,char,text)
        self.event_dispatch("set_text",(char,text))

    def set_text_s(self,text):
        self.set_text("s", text)
    
    def register_char(self, name, color, showname):
        self.registeredChars[name] = [color, showname]
    
    def texts_next(self, nexttexts, endfunc):
        self.nextTexts = nexttexts
        self.endNextTextFunc = endfunc
        self.NextText(self)
        
    def NextText(self, game):
        if(len(self.nextTexts) > 0):
            self.set_text(self.nextTexts[0][0],self.nextTexts[0][1])
            self.set_buttons([self.btnNextText],[self.NextText])
            if(len(self.nextTexts[0]) > 2):
                func = self.nextTexts[0][2]
                func(self,self.nextTexts[0][3])
            self.nextTexts.pop(0)
        else:
            self.endNextTextFunc(self)
    
    def show_window(self):
        self.visible = True
        
    def hide_window(self):
        self.visible = False
    
    def show_blocking_message(self, text = "..."):
        self.funcLockedText = text
        self.isFuncLocked = True
        
    def hide_blocking_message(self,game=None):
        self.isFuncLocked = False

    def show_blocking_message_time(self, text = "...", duration = 3):
        self.show_blocking_message(text)
        self.set_timer(duration, self.hide_blocking_message)
        
        
    # simulating key presses
    def anim_sim_zoom_in(self,duration):
        InputSimulator.SimulateKeyDown(VirtualKeyCode.UP)
        self.set_timer(duration,self._anim_sim_zoom_in_end)

    def _anim_sim_zoom_in_end(self,game):
        InputSimulator.SimulateKeyUp(VirtualKeyCode.UP)
        
    def anim_sim_zoom_out(self,duration):
        InputSimulator.SimulateKeyDown(VirtualKeyCode.DOWN)
        self.set_timer(duration,self._anim_sim_zoom_out_end)

    def _anim_sim_zoom_out_end(self,game):
        InputSimulator.SimulateKeyUp(VirtualKeyCode.DOWN)
        
    # reseting scene - must be overrided by engine
    def reset(self):
        return
    
    def call_game_func(self,param):
        try:
            #print param
            if param == None:
                return

            if isinstance(param,tuple):
                if len(param) == 2: # (?, ?)
                    if isinstance(param[0],list): # ([func] param)
                        param[0](param[1])
                    else:
                        param[0](self,param[1]) # (func param)
                    return
            elif isinstance(param,list): # [func]
                    #print "new call"
                    param[0]()
            else:
                # default - call func(game)
                param(self)
        except Exception, e:
            print "Error in call_game_func: "+str(e)


    def load_scene(self, file):
        """Load scene from file"""
        self.show_blocking_message_time("ERROR: load_scene was not implemented")

    def get_scene_dir(self):
        """Return dir, where engine saves scenes"""
        # must be implemented in child
        return ""

    def get_camera_num(self, camnum):
        self.show_blocking_message_time("ERROR: get_camera_num was not implemented")
        return self.camparams2vec((0,0,0), (0,0,0), (0,0,0))

    def anim_to_camera_num(self, duration, camnum, style="linear", onCameraEnd=None):
        """
        Made animation movement to camera with some number
        :param float duration: Duration of animation in seconds
        :param int camnum: Camera number to animate from current position
        :param * style: may be an object or string. String can be linear,slow-fast,fast-slow,slow-fast3,fast-slow3,slow-fast4,fast-slow4. Object may vary
        :param Callable onCameraEnd: function that wil called after animation end
        """
        #self.show_blocking_message_time("ERROR: anim_to_camera_num was not implemented")
        self.anim_to_camera_obj(duration,self.get_camera_num(camnum),style,onCameraEnd)

    def scene_get_all_females(self):
        return []

    def scene_get_all_males(self):
        return []

    def debug_print_all_chars(self):
        fems = self.scene_get_all_females()
        print("-- Female scene chars: --")
        for i in range(len(fems)):
            print "%s: %s" % (str(i), fems[i].text_name)
        fems = self.scene_get_all_males()
        print("-- Male scene chars: --")
        for i in range(len(fems)):
            print "%s: %s"%(str(i),fems[i].text_name)
        self.show_blocking_message_time("Debug: list of chars printed in console!")

    # ---------- menu functions -------------------------
    def run_menu(self,menufunc,menuparam,onEndFunc):
        self._menuStack.append(onEndFunc)
        menufunc(self,menuparam)

    def menu_finish(self,result):
        self.menu_result = result
        endFunc = self._menuStack.pop()
        self.call_game_func(endFunc)

    # ---------- checking for engine types --------------
    @property
    def isClassicStudio(self):
        return self.engine_name == "studio"
        
    @property
    def isStudioNEO(self):
        return self.engine_name == "neo"

    @property
    def isNEOV2(self):
        return self.engine_name == "neov2"

    @property
    def isCharaStudio(self):
        return self.engine_name == "charastudio"

    @property
    def isPlayHomeStudio(self):
        return self.engine_name == "phstudio"

    # -------- other ----------
    def scene_set_bg_png(self, filepng):
        self.show_blocking_message_time("ERROR: scene_set_bg_png was not implemented")

    # ---------- cameras ----------
    def move_camera(self, pos=None, distance=None, rotate=None, fov=23.0):
        #self.show_blocking_message_time("ERROR: move_camera was not implemented")
        camobj = self.camparams2vec(pos,distance,rotate,fov)
        self.move_camera_obj(camobj)

    def move_camera_obj(self,camobj):
        camv = self.cam2vec(camobj)
        self.move_camera_direct(camv["pos"], camv["distance"], camv["rotate"], camv["fov"])

    def camparams2vec(self, pos, distance, rotate, fov=23.0):
        obj = {}
        if pos:
            if isinstance(pos, tuple) and len(pos) == 3:
                pos = Vector3(pos[0], pos[1], pos[2])
            if isinstance(pos, Vector3):
                obj["pos"] = pos
        if distance:
            if isinstance(distance, tuple) and len(distance) == 3:
                distance = Vector3(distance[0], distance[1], distance[2])
            if isinstance(distance, Vector3):
                obj["distance"] = distance
        if rotate:
            if isinstance(rotate, tuple) and len(rotate) == 3:
                rotate = Vector3(rotate[0], rotate[1], rotate[2])
            if isinstance(rotate, Vector3):
                obj["rotate"] = rotate
        obj["fov"] = fov
        return obj

    def cam2vec(self,camobj):
        return self.camparams2vec(camobj["pos"], camobj["distance"], camobj["rotate"], camobj["fov"])

    def move_camera_direct(self, pos=None, distance=None, rotate=None, fov=23.0):
        # expecting only Vectors3
        self.show_blocking_message_time("ERROR: move_camera_direct was not implemented")

    def anim_to_camera(self, duration, pos=None, distance=None, rotate=None, fov=23.0, style="linear", onCameraEnd=None):
        camobj = self.camparams2vec(pos,distance,rotate,fov)
        self.anim_to_camera_obj(duration,camobj,style,onCameraEnd)

    def anim_to_camera_obj(self, duration, camobj, style="linear",
                           onCameraEnd=None):
        self._anim_to_camera_savecurrentpos()
        # print "Anim to cam 1"
        # print "Anim to cam 2"
        camobjv = self.cam2vec(camobj)
        self.camTPos = camobjv["pos"]
        self.camTDir = camobjv["distance"]
        self.camTAngle = camobjv["rotate"]
        self.camTFOV = camobjv["fov"]

        if isinstance(style, str):
            self.camAnimStyle = style
            self.camAnimFullStyle = None
        else:
            self.camAnimStyle = style["style"]
            self.camAnimFullStyle = style
            if 'add_distance_target_camera' in self.camAnimFullStyle:
                self.camTDir = Vector3(self.camTDir.x, self.camTDir.y,
                                       self.camTDir.z + self.camAnimFullStyle["add_distance_target_camera"])

        # camera animation one timer only
        if self.camAnimeTID != -1:
            self.clear_timer(self.camAnimeTID)
        self.camAnimeTID = self.set_timer(duration, self._anim_to_cam_end, self._anim_to_cam_upd)

        self._onCameraEnd = onCameraEnd

        if self.isHideWindowDuringCameraAnimation:
            self.hide_window()

    def _anim_to_cam_upd(self, game, dt, time, duration):

        camProgress = time / duration

        if self.camAnimStyle == "linear":
            camProgress = time / duration
        if self.camAnimStyle == "slow-fast":
            camProgress = Mathf.Pow(camProgress, 2)
        if self.camAnimStyle == "fast-slow":
            camProgress = 1 - Mathf.Pow(1 - camProgress, 2)
        if self.camAnimStyle == "slow-fast3":
            camProgress = Mathf.Pow(camProgress, 3)
        if self.camAnimStyle == "fast-slow3":
            camProgress = 1 - Mathf.Pow(1 - camProgress, 3)
        if self.camAnimStyle == "slow-fast4":
            camProgress = Mathf.Pow(camProgress, 4)
        if self.camAnimStyle == "fast-slow4":
            camProgress = 1 - Mathf.Pow(1 - camProgress, 4)


        TPos = self.camTPos
        TDir = self.camTDir
        TAngle = self.camTAngle

        if self.camAnimFullStyle != None:
            if 'target_camera_zooming_in' in self.camAnimFullStyle:
                TDir = Vector3(TDir.x, TDir.y,
                               TDir.z - self.camAnimFullStyle["target_camera_zooming_in"] * (1 - time / duration))
            if 'target_camera_rotating_z' in self.camAnimFullStyle:
                TAngle = Vector3(TAngle.x,
                                 TAngle.y,
                                 TAngle.z + self.camAnimFullStyle["target_camera_rotating_z"] * (1 - time / duration))
            if 'target_camera_rotating_x' in self.camAnimFullStyle:
                TAngle = Vector3(TAngle.x + self.camAnimFullStyle["target_camera_rotating_x"] * (1 - time / duration),
                                 TAngle.y,
                                 TAngle.z )
            if 'target_camera_rotating_y' in self.camAnimFullStyle:
                TAngle = Vector3(TAngle.x,
                                 TAngle.y + self.camAnimFullStyle["target_camera_rotating_y"] * (1 - time / duration),
                                 TAngle.z)
            if 'target_camera_posing_y' in self.camAnimFullStyle:
                TPos = Vector3(TPos.x,
                               TPos.y + self.camAnimFullStyle["target_camera_posing_y"] * (1 - time / duration),
                               TPos.z)
                # TDir.z = TDir.z + self.camAnimFullStyle["move_distance"] * time / duration
                # TDir.z = TDir.z + (-20)
                # print "z: %s"%(str(TDir.z))

        pos = Vector3.Lerp(self.camSPos, TPos, camProgress)
        distance = Vector3.Lerp(self.camSDir, TDir, camProgress)
        rotate = Vector3.Slerp(self.camSAngle, TAngle, camProgress)
        fov = Mathf.Lerp(self.camSFOV, self.camTFOV, camProgress)
        #print fov, self.camSFOV, self.camTFOV, camProgress
        self.move_camera_direct(pos, distance, rotate, fov)

    def _anim_to_cam_end(self, game):
        # game.set_text("Anim camera end!")
        # print "Anim camera end!"
        if self.isHideWindowDuringCameraAnimation:
            self.show_window()
        self.camAnimeTID = -1
        self.call_game_func(self._onCameraEnd)
        return

    def _anim_to_camera_savecurrentpos(self):
        camobj = self.get_camera_num(0)
        self.camSPos = camobj["pos"]
        self.camSDir = camobj["distance"]
        self.camSAngle = camobj["rotate"]
        self.camSFOV = camobj["fov"]

    def vec3(self,x,y,z):
        return Vector3(x,y,z)

    # ---- automaking list of games -----
    def prepare_auto_games(self,game):
        self.prepare_auto_games_prefix(game,"")

    def prepare_auto_games_prefix(self,game,prefix):
        from os import listdir
        from os.path import isfile, join
        mypath = self.pygamepath
        onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]

        arTx = []
        arTx2 = []
        arAc = []

        for fil in onlyfiles:
            #print fil[:-3]
            if fil.endswith(".py"):
                firstline = self.file_get_firstline(mypath+"\\"+fil)
                #print firstline
                ar = firstline.split(";")
                # checking header
                if len(ar) >= 3:
                    if ar[0] == "#vngame":
                        if (ar[1] == "all") or (ar[1] == self.engine_name):
                            # found game!
                            #print "-- Found game! --"
                            gamename = ar[2]
                            if gamename[-1] == "\n":
                                gamename = gamename[:-1]

                            # Add game or folder if prefix matches. Unless the name already exists.
                            # (An empty prefix will match all games)
                            if gamename.startswith(prefix):
                                # Remove prefix from gamename
                                if len(prefix) > 0:
                                    gamename = gamename[len(prefix):]
                                # If gamename looks like a folder, just keep the foldername
                                if '/' in gamename:
                                    gamename = gamename.split("/")[0]
                                    gamename = gamename + "/"
                                if not gamename in arTx2:
                                    arTx2.append(gamename)
                                    if '/' in gamename:
                                        arTx.append("<color=#aaaaaaff>"+gamename[:-1]+ " ></color>")
                                        arAc.append((self.prepare_auto_games_prefix, prefix + gamename))
                                    else:
                                        arTx.append(gamename)
                                        arAc.append((self.game_start_fromfile, fil[:-3]))

        # IF we have a prefix, add button to go back one level
        if len(prefix) > 0:
            parent = "/".join(prefix.split("/")[:-2]) # Get parent-prefix
            # Add a / but only if the prefix we want to go to is non-empty
            if parent:
                parent = parent + "/"
            arTx.append("<< Back")
            arAc.append((self.prepare_auto_games_prefix, parent))

        self.set_text_s("Available games:")
        self.set_buttons(arTx,arAc)


    def game_start_fromfile(self,game,gamefilestr):
        oldcurrentgame = self.current_game
        print "-- Importing and starting game: %s --"%(gamefilestr)
        try:
            self.current_game = gamefilestr
            importOrReload(gamefilestr)
            import sys
            sys.modules[gamefilestr].start(self)

        except Exception, e:
            print("Error in game loading: " + str(e))
            import traceback
            print "details:"
            traceback.print_exc()

            self.show_blocking_message_time("ERROR: can't load or start game '%s'"%(gamefilestr))
            self.current_game = oldcurrentgame

        #__import__(gamefilestr).start(self)


    def file_get_firstline(self,filename):
        import os
        if os.path.exists(filename):
            fp = open(filename, "r")
            content = fp.readline()
            fp.close()
            return content
        return ""

    def file_get_firstline_utf8(self,filename):
        import os
        import codecs
        if os.path.exists(filename):
            fp = codecs.open(filename, "r", encoding="utf-8")
            content = fp.readline()
            fp.close()
            return content
        return ""

    def file_get_content(self,filename):
        import os
        if os.path.exists(filename):
            fp = open(filename, "r")
            content = fp.read()
            fp.close()
            return content
        return ""

    def file_get_content_utf8(self,filename):
        import os
        import codecs
        if os.path.exists(filename):
            fp = codecs.open(filename, "r", encoding="utf-8")
            content = fp.read()
            fp.close()
            return content
        return ""


    # ------------ event system -----------
    def event_reg_listener(self,eventid,func):
        self._event_create_lisarray_ifneeded(eventid)
        self._eventListenerDic[eventid].append(func)

    def _event_create_lisarray_ifneeded(self,eventid):
        if eventid in self._eventListenerDic:
            return
        else:
            self._eventListenerDic[eventid] = []

    def event_unreg_listener(self,eventid,func):
        self._event_create_lisarray_ifneeded(eventid)
        if func in self._eventListenerDic[eventid]:
            self._eventListenerDic[eventid].remove(func)
            return True
        return False

    def event_dispatch(self,eventid,param):
        if eventid in self._eventListenerDic:
            for func in self._eventListenerDic[eventid]:
                func(self,eventid,param)

    def _load_scene_before(self,file):
        self._unload_scene_before()
        self.event_dispatch("before_scene_load", file)

    def _unload_scene_before(self):
        self.event_dispatch("before_scene_unload", None)
        self.scenedata = GData()

    # -------- game persistent data ----------
    def gpersdata_getfilename(self):
        from os import path
        dstfile = path.join(self.pygamepath, "Gpdata", self.current_game+"_p.dat")
        return dstfile

    def gpersdata_exists(self):
        dstfile = self.gpersdata_getfilename()
        from os import path
        return path.isfile(dstfile)

    def gpersdata_load(self):
        try:
            dstfile = self.gpersdata_getfilename()
            if self.gpersdata_exists():
                with open(dstfile, "r") as f2:
                    import pickle
                    self.gpersdata = pickle.load(f2)
                    f2.close()
                msg = "gpersdata loaded!"
            else:
                msg = "gpersdata not exists!"
        except Exception as e:
            msg = "gpersdata load Failed: " + str(e)
        # game.show_blocking_message_time(msg)
        print msg

    def gpersdata_save(self):
        try:
            dstfile = self.gpersdata_getfilename()
            with open(dstfile, "w") as f:
                import pickle
                pickle.dump(self.gpersdata, f)
                f.close()
                return ""
        except Exception as e:
            msg = "gpersdata save Failed: " + str(e)
            print msg
            return msg
        # game.show_blocking_message_time(msg)


    def gpersdata_set(self,param,val):
        self.gpersdata[param] = val
        return self.gpersdata_save()

    def gpersdata_get(self,param):
        if param in self.gpersdata:
            return self.gpersdata[param]
        else:
            return None

    def gpersdata_clear(self):
        import os
        try:
            dstfile = self.gpersdata_getfilename()
            os.remove(dstfile)
            self.gpersdata = {}
            msg = "gpersdata cleared!"
        except Exception as e:
            msg = "gpersdata clear Failed: " + str(e)
        print msg

    # ---------- checkpoints --------
    def checkpoint_set_list(self,type,arr):
        if not hasattr(self.gdata, '_check_list'):
            self.gdata._check_list = {}

        self.gdata._check_list[type] = arr

    def checkpoint_save(self,type,checkId):
        arr = self.checkpoint_loadall(type)
        if checkId in arr:
            pass
            return ""
        else:
            arr.append(checkId)
            return self.gpersdata_set("_checkpoints_"+type, arr)

    def checkpoint_loadall(self,type):
        res = self.gpersdata_get("_checkpoints_"+type)
        if res == None:
            return []
        else:
            return res

    def checkpoint_clean(self,type):
        self.gpersdata_set("_checkpoints_" + type, [])

    def checkpoint_goto(self,type,checkId):
        arr = self.gdata._check_list[type]
        for checkpoint in arr:
            if checkpoint[0] == checkId:
                self.call_game_func(checkpoint[1])
                return True
        return False

    def checkpoint_has_one(self,type):
        return len(self.checkpoint_loadall(type)) > 0

    def checkpoint_goto_latest(self,type):
        arr = self.checkpoint_loadall(type)
        if len(arr) > 0:
            return self.checkpoint_goto(type,arr[-1])
        return False

    def checkpoint_rendergotomenu(self,type,showall=True):
        btns = []
        arr = self.gdata._check_list[type]
        arrpassed = self.checkpoint_loadall(type)
        for checkpoint in arr:
            checkId = checkpoint[0]
            if checkId in arrpassed:
                btns += [checkpoint[2][0],(self._checkpoint_goto_menu,(type,checkId))]
            else:
                if showall:
                    txt = '<color=#{1}ff>{0}</color>'.format(checkpoint[2][0], '666666')
                    btns += [txt, None]
        return btns

    def _checkpoint_goto_menu(self,game,param):
        self.checkpoint_goto(param[0],param[1])

    # --------- skin system ------------
    def skin_set(self, skin):
        self.skin = skin
        self.skin.setup(self)

    def skin_set_byname(self,skinname):
        try:
            mod = import_or_reload(skinname)
            skin = mod.get_skin()
            self.skin_set(skin)
        except Exception, e:
            print "Error loading skin ", skinname, ", error: ", e

    def skin_get_current(self):
        return self.skin

def vngame_window_studio(vnButtonsStart, vnButtonsActionsStart):
    import vngameenginestudio
    game = vngameenginestudio.vngame_window_studio(vnButtonsStart, vnButtonsActionsStart)
    return game

class HSNeoOCI(object):    
    def __init__(self, objctrl):
        self.objctrl = objctrl
        return
    
    #changed by chickenman
    @staticmethod
    def create_from(objctrl):
        from Studio import OCIFolder, OCIChar, Studio, OCIItem, OCILight
        if isinstance(objctrl,OCIFolder):
            return HSNeoOCIFolder(objctrl)
        if isinstance(objctrl,OCIChar):
            return HSNeoOCIChar(objctrl)
        if isinstance(objctrl,OCIItem):
            return HSNeoOCIItem(objctrl)
        if isinstance(objctrl,OCILight):
            return HSNeoOCILight(objctrl)
        return HSNeoOCI(objctrl)


    @staticmethod
    def create_from_treenode(treenode):
        if treenode == None:
            return None

        from Studio import OCIFolder, OCIChar, Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""

        dobjctrl = studio.dicInfo
        obj = dobjctrl[treenode]
        if obj != None:
            return HSNeoOCI.create_from(obj)
        return None

    @staticmethod
    def create_from_selected():
        from Studio import OCIFolder, OCIChar, Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""
        return HSNeoOCI.create_from_treenode(studio.treeNodeCtrl.selectNode)

    @property
    def visible_treenode(self):
        return self.objctrl.treeNodeObject.visible

    @visible_treenode.setter
    def visible_treenode(self,value):
        if self.objctrl.treeNodeObject.visible != value:
            self.objctrl.treeNodeObject.SetVisible(value)

    @property
    def treeNodeObject(self):
        obj = self.objctrl.treeNodeObject
        """ :type : dummyneoclasses.TreeNodeObject"""

        return obj

    def set_parent_treenodeobject(self,parentTreeNode):
        from Studio import Studio
        studio = Studio.Instance
        studio.treeNodeCtrl.SetParent(self.treeNodeObject, parentTreeNode)

    def set_parent(self,parent):
        self.set_parent_treenodeobject(parent.treeNodeObject)

    def delete(self):
        from Studio import Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""
        studio.treeNodeCtrl.DeleteNode(self.treeNodeObject)

    @property
    def text_name(self):
        return self.objctrl.treeNodeObject.textName

class HSNeoOCIProp(HSNeoOCI):

    globalPropClass = None

    @property
    def as_prop(self):
        from vnactor import Prop,PropHSNeo,PropCharaStudio,PropPHStudio,PropNeoV2
        # we are Prop already
        if isinstance(self, Prop):
            return self

        # in case we want special class handling
        if HSNeoOCIProp.globalPropClass:
            return HSNeoOCIProp.globalPropClass(self.objctrl)

        # make prop depending on engine
        enid = get_engine_id()
        obj = None
        if enid == "neo":
            obj = PropHSNeo(self.objctrl)
        if enid == "phstudio":
            obj = PropPHStudio(self.objctrl)
        if enid == "charastudio":
            obj = PropCharaStudio(self.objctrl)
        if enid == "neov2":
            obj = PropNeoV2(self.objctrl)

        return obj

class HSNeoOCIFolder(HSNeoOCIProp):
    @staticmethod
    def add(name = None):
        from Studio import AddObjectFolder
        fold = AddObjectFolder.Add()
        obj = HSNeoOCIFolder(fold)
        if name != None:
            obj.name = name
        return obj

    @staticmethod
    def find_all(name=None):
        from Studio import OCIFolder, Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""
        ar = []
        dobjctrl = studio.dicInfo
        for key in dobjctrl.Keys:  # key is TreeNodeObject
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIFolder):
                txt = objctrl.name
                # ar.append((objctrl.name, objctrl, key))
                if txt == name:
                    ar.append(HSNeoOCIFolder(objctrl))
                    # this will process for all folders we found

        return ar

    @staticmethod
    def find_single(name=None):
        ar = HSNeoOCIFolder.find_all(name)
        obj = None
        """ :type : HSNeoOCIFolder"""
        if len(ar) > 0:
            obj = ar[0]
        return obj

    @staticmethod
    def find_all_startswith(name=None):
        from Studio import OCIFolder, Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""
        ar = []
        dobjctrl = studio.dicInfo
        for key in dobjctrl.Keys:  # key is TreeNodeObject
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIFolder):
                txt = objctrl.name
                """ :type txt: string """
                if txt.startswith(name):
                    ar.append(HSNeoOCIFolder(objctrl))
                    # this will process for all folders we found

        return ar

    @staticmethod
    def find_single_startswith(name=None):
        ar = HSNeoOCIFolder.find_all_startswith(name)
        obj = None
        """ :type : HSNeoOCIFolder"""
        if len(ar) > 0:
            obj = ar[0]
        return obj

    @property
    def name(self):
        return self.objctrl.name

    @name.setter
    def name(self,value):
        self.objctrl.name = value

    def delete_all_children(self):
        ar = self.treeNodeObject.child
        ar2 = []
        for treeobj in ar:
            ar2.append(HSNeoOCI.create_from_treenode(treeobj))

        for obj in ar2:
            obj.delete()

    @property
    def pos(self):
        return self.objctrl.objectInfo.changeAmount.pos

    @property
    def rot(self):
        return self.objctrl.objectInfo.changeAmount.rot

    def set_pos(self, param):
        # param = (pos_x, pos_y, pos_z)
        item = self.objctrl
        ncp = Vector3(param[0], param[1], param[2])
        item.objectInfo.changeAmount.pos = ncp

    def set_rot(self, param):
        # param = (rot_x, rot_y, rot_z)
        try:
            item = self.objctrl
            nrt = Vector3(param[0], param[1], param[2])
            item.objectInfo.changeAmount.rot = nrt
        except Exception, e:
            print "Error in set_rot %s"%(str(e))

class HSNeoOCIItem(HSNeoOCIProp):
    def __init__(self, objctrl):
        self.objctrl = objctrl
        """ :type : dummyneoclasses.OCIItem"""
        return

    @staticmethod
    def add_item(no):
        from Studio import AddObjectItem
        objctrl = AddObjectItem.Add(no)
        return HSNeoOCIItem(objctrl)

    def pos_add(self, param):
        # param = (pos_delta_x, pos_delta_y, pos_delta_z)
        item = self.objctrl
        cp = item.objectInfo.changeAmount.pos
        ncp = Vector3(cp.x + param[0], cp.y + param[1], cp.z + param[2])
        item.objectInfo.changeAmount.pos = ncp

    def set_pos(self, param):
        # param = (pos_x, pos_y, pos_z)
        item = self.objctrl
        ncp = Vector3(param[0], param[1], param[2])
        item.objectInfo.changeAmount.pos = ncp

    def rot_add(self, param):
        # param = (rot_delta_x, rot_delta_y, rot_delta_z)
        item = self.objctrl
        rt = item.objectInfo.changeAmount.rot
        nrt = Vector3(rt.x + param[0], rt.y + param[1], rt.z + param[2])
        item.objectInfo.changeAmount.rot = nrt

    def set_rot(self, param):
        # param = (rot_x, rot_y, rot_z)
        item = self.objctrl
        nrt = Vector3(param[0], param[1], param[2])
        item.objectInfo.changeAmount.rot = nrt

    def scale_add(self, param):
        # param = (scale_x, scale_y, scale_z) or scale
        item = self.objctrl
        from Studio import OCIItem
        if isinstance(item, OCIItem):
            # for item only, folder can not set scale
            rt = item.itemInfo.changeAmount.scale
            nrt = Vector3(rt.x + param[0], rt.y + param[1], rt.z + param[2])
            item.itemInfo.changeAmount.scale = nrt

    def set_scale(self, param):
        # param = (scale_x, scale_y, scale_z) or scale
        item = self.objctrl
        from Studio import OCIItem
        if isinstance(item, OCIItem):
            # for item only, folder can not set scale
            if isinstance(param, tuple):
                nsl = Vector3(param[0], param[1], param[2])
            else:
                nsl = Vector3(param, param, param)
            item.itemInfo.changeAmount.scale = nsl

    @property
    def pos(self):
        return self.objctrl.itemInfo.changeAmount.pos

    @property
    def rot(self):
        return self.objctrl.itemInfo.changeAmount.rot

    @property
    def scale(self):
        return self.objctrl.itemInfo.changeAmount.scale

    @property
    def no(self):
        return self.objctrl.itemInfo.no

#changed by chickenman
class HSNeoOCILight(HSNeoOCIProp):
    def __init__(self, objctrl):
        self.objctrl = objctrl
        """ :type : dummyneoclasses.OCILight"""
        return
    
    @staticmethod
    def add_light(no): #no:0~8
        from Studio import AddObjectLight
        objctrl = AddObjectLight.Add(no)
        return HSNeoOCILight(objctrl)
    
    def pos_add(self, param):
        # param = (pos_delta_x, pos_delta_y, pos_delta_z)
        item = self.objctrl
        cp = item.objectInfo.changeAmount.pos
        ncp = Vector3(cp.x + param[0], cp.y + param[1], cp.z + param[2])
        item.objectInfo.changeAmount.pos = ncp

    def set_pos(self, param):
        # param = (pos_x, pos_y, pos_z)
        item = self.objctrl
        ncp = Vector3(param[0], param[1], param[2])
        item.objectInfo.changeAmount.pos = ncp

    def rot_add(self, param):
        # param = (rot_delta_x, rot_delta_y, rot_delta_z)
        item = self.objctrl
        rt = item.objectInfo.changeAmount.rot
        nrt = Vector3(rt.x + param[0], rt.y + param[1], rt.z + param[2])
        item.objectInfo.changeAmount.rot = nrt

    def set_rot(self, param):
        # param = (rot_x, rot_y, rot_z)
        item = self.objctrl
        nrt = Vector3(param[0], param[1], param[2])
        item.objectInfo.changeAmount.rot = nrt
    
    @property
    def enable(self):
        return self.objctrl.lightInfo.enable
    
    @property
    def type(self):
        return self.objctrl.lightType
    
    @property
    def no(self):
        return self.objctrl.lightInfo.no
    
    @property
    def pos(self):
        return self.objctrl.objectInfo.changeAmount.pos

    @property
    def rot(self):
        return self.objctrl.objectInfo.changeAmount.rot
    
class HSNeoOCIChar(HSNeoOCI):
    globalActorClass = None

    def __init__(self, objctrl):
        self.objctrl = objctrl
        """ :type : dummyneoclasses.OCIChar"""
        return

    @staticmethod
    def add_female(path):
        from Studio import AddObjectFemale
        objctrl = AddObjectFemale.Add(path)
        return HSNeoOCIChar(objctrl)

    @staticmethod
    def add_male(path):
        from Studio import AddObjectMale
        objctrl = AddObjectMale.Add(path)
        return HSNeoOCIChar(objctrl)

    @property
    def as_actor(self):
        from vnactor import Actor,ActorHSNeo,ActorPHStudio,ActorCharaStudio,ActorNeoV2
        if isinstance(self,Actor):
            return self

        if HSNeoOCIChar.globalActorClass:
            return HSNeoOCIChar.globalActorClass(self.objctrl)

        enid = get_engine_id()
        obj = None
        if enid == "neo":
            obj = ActorHSNeo(self.objctrl)
        if enid == "phstudio":
            obj = ActorPHStudio(self.objctrl)
        if enid == "charastudio":
            obj = ActorCharaStudio(self.objctrl)
        if enid == "neov2":
            obj = ActorNeoV2(self.objctrl)

        return obj

    @property
    def charInfo(self):
        return self.objctrl.charInfo

    @property
    def oiCharInfo(self):
        return self.objctrl.oiCharInfo
        
    @property
    def pos(self):
        return self.charInfo.transform.localPosition
        
    @property
    def rot(self):
        return self.charInfo.transform.localRotation.eulerAngles
   
    @property
    def scale(self):
        return self.charInfo.transform.localScale   

    @property
    def look_eyes_ptn(self):
        try:
            return self.objctrl.charInfo.GetLookEyesPtn()
        except Exception, e:
            print str(e)
        return ""

    @look_eyes_ptn.setter
    def look_eyes_ptn(self,value):
        self.objctrl.charInfo.ChangeLookEyesPtn(value)

    @property
    def look_neck_ptn(self):
        try:
            return self.objctrl.charInfo.GetLookNeckPtn()
        except Exception, e:
            print str(e)
        return ""

    @look_neck_ptn.setter
    def look_neck_ptn(self, value):
        self.objctrl.charInfo.ChangeLookNeckPtn(value)

    @property
    def mouth_ptn(self):
        try:
            return self.objctrl.charInfo.GetMouthPtn()
        except Exception, e:
            print str(e)
        return ""

    @mouth_ptn.setter
    def mouth_ptn(self, value):
        self.objctrl.charInfo.ChangeMouthPtn(value)

    @property
    def mouth_openmax(self):
        try:
            return self.objctrl.charInfo.GetMouthOpenMax()
        except Exception, e:
            print str(e)
        return ""

    @mouth_openmax.setter
    def mouth_openmax(self, value):
        self.objctrl.charInfo.ChangeMouthOpenMax(value)

    @property
    def eyes_ptn(self):
        try:
            return self.objctrl.charInfo.GetEyesPtn()
        except Exception, e:
            print str(e)
        return ""

    @eyes_ptn.setter
    def eyes_ptn(self, value):
        self.objctrl.charInfo.ChangeEyesPtn(value)

    @property
    def eyes_openmax(self):
        try:
            return self.objctrl.charInfo.GetEyesOpenMax()
        except Exception, e:
            print str(e)
        return ""

    @eyes_openmax.setter
    def eyes_openmax(self, value):
        self.objctrl.charInfo.ChangeEyesOpenMax(value)

    @property
    def eyebrow_ptn(self):
        try:
            return self.objctrl.charInfo.GetEyebrowPtn()
        except Exception, e:
            print str(e)
        return ""

    @eyebrow_ptn.setter
    def eyebrow_ptn(self, value):
        self.objctrl.charInfo.ChangeEyebrowPtn(value)

    @property
    def tears_level(self):
        try:
            return self.objctrl.GetTearsLv()
        except Exception, e:
            print str(e)
        return ""

    @tears_level.setter
    def tears_level(self, value):
        self.objctrl.SetTearsLv(value)

    def move(self, pos=None, rot=None, scale=None):
        #if not self.stChara: return False
        
        from UnityEngine import Vector3
        if pos:
            if isinstance(pos, tuple) and len(pos) == 3:
                pos = Vector3(pos[0], pos[1], pos[2])
            #self.stChara.objCtrl.transform.localPosition = pos
            #self.charInfo.SetPosition(pos);
            self.charInfo.transform.localPosition = pos
            
        if rot:
            if isinstance(rot, tuple) and len(rot) == 3:
                rot = Vector3(rot[0], rot[1], rot[2])
            #self.charInfo.SetRotation(rot)
            self.charInfo.transform.localRotation.eulerAngles = rot
            
        if scale:
            if isinstance(scale, tuple) and len(scale) == 3:
                scale = Vector3(pos[0], pos[1], pos[2])
            #self.stChara.objCtrl.transform.localPosition = pos
            #self.objctrl.charInfo.chaBody.objAnim.transform.localScale = localScale; 
            self.charInfo.transform.localScale = scale
            
        return True

    
    def animate(self,group,category,no,animePattern,animeSpeed):
        #print "1"
        try:
            self.oiCharInfo.animePattern = animePattern
        except Exception, e:
            # passing for PlayHome Studio - it has another pattern
            pass
        #print "2"
        #
        self.objctrl.LoadAnime(group, category, no, animeSpeed);
        #print "3"
        self.objctrl.animeSpeed = animeSpeed
        #self.charInfo.chaBody.animBody.speed = animeSpeed
        #print "4"
        #self.charInfo.chaBody.animBody.speed = animeSpeed
        #print "5"
        #self.objctrl.RestartAnime()
        #print "6"

    def animate2(self,group,category,no,animeSpeed):
        self.objctrl.LoadAnime(group, category, no);
        self.objctrl.animeSpeed = animeSpeed

    def restart_anime(self):
        self.objctrl.RestartAnime()

    #def animate2(self,category,group,no,animePattern,animeSpeed):
    # debug purpose - load info from dictionary
    def animate_some_info(self,group,category,no,animePattern,animeSpeed):
        #from Studio import Info
        #Info.Instance.LoadExcelData()
        
        print "21"
        #print str(Info.Instance.dicFemaleAnimeLoadInfo)
        dic0 = Info.Instance.dicFemaleAnimeLoadInfo
        for key in dic0.Keys:
            print key
        d1 = dic0[category]
        print str(d1)
        d2 = d1[group]
        print str(d2)
        animeLoadInfo = d2[no]
        print str(animeLoadInfo)
        print "%s %s %s"%(animeLoadInfo.bundlePath, animeLoadInfo.fileName, animeLoadInfo.clip)
        
        
    def female_all_clothes_state(self,state):
        self.objctrl.SetClothesStateAll(state)

        
    def dump_obj(self):
        #print "objctrlchar.move(pos=%s, rot=%s)"%(str(self.charInfo.GetPosition()), str(self.charInfo.GetRotation()))
        try:
            print "objctrlchar.move(pos=%s, rot=%s, scale=%s)"%(str(self.charInfo.transform.localPosition), str(self.charInfo.transform.localRotation.eulerAngles), str(self.charInfo.transform.localScale))
            print "objctrlchar.animate(%s, %s, %s, %s, %s)"%(str(self.oiCharInfo.animeInfo.group), str(self.oiCharInfo.animeInfo.category), str(self.oiCharInfo.animeInfo.no), str(self.oiCharInfo.animePattern), str(self.oiCharInfo.animeSpeed))
            #print "objctrlchar.tears_level = %s" % (str(self.tears_level))
        except Exception, e:
            print "# oops, error happened %s"%str(e)
        return


        
class VNNeoController(VNController): # for all engines, who derived NEO engine
    def __init__(self):
        VNController.__init__(self)

    def calc_py_path(self):
        from UnityEngine import Application
        from os import path
        rootfolder = path.realpath(path.join(Application.dataPath, '..'))

        # os.path.splitext(__file__)[0] + '.ini'
        pydirname = path.dirname(__file__)
        return path.relpath(pydirname, rootfolder)

        
    def to_camera(self,camnum):
        if self.isCharaStudio:
            # old code simulating key press
            ar = [VirtualKeyCode.VK_1,VirtualKeyCode.VK_2,VirtualKeyCode.VK_3,VirtualKeyCode.VK_4,VirtualKeyCode.VK_5,VirtualKeyCode.VK_6,VirtualKeyCode.VK_7,VirtualKeyCode.VK_8,VirtualKeyCode.VK_9,VirtualKeyCode.VK_0]
            InputSimulator.SimulateKeyPress(ar[camnum-1])
            # enable it due to fucking reason - in CharaStudio camera not always setting at correct position
        else:
            from Studio import Studio
            studio = Studio.Instance
            si = studio.sceneInfo
            cdatas = si.cameraData

            c = studio.cameraCtrl
            cdata = c.cameraData
            cdata.Copy(cdatas[camnum-1])
        """
        camobj = self.get_camera_num(camnum)
        self.move_camera_obj(camobj)
        """
    def move_camera_direct(self, pos=None, distance=None, rotate=None, fov=None):
        c = self.studio.cameraCtrl
        cdata = c.cameraData
        if pos:
            cdata.pos = pos
        if distance:
            cdata.distance = distance
        if rotate:
            cdata.rotate = rotate

        if fov:
            if c.fieldOfView != fov:
                c.fieldOfView = fov

    def hsneo_dump_camera(self):
        #import hs
        f = open('dumppython.txt', 'a+')
        import sys
        tmp = sys.stdout
        sys.stdout = f
        print("---DUMP! Camera----")
        #hs.HSCamera.dump()
        self.hsneo_dump_camera2()
        print("")
        sys.stdout = tmp
        f.close()
        self.show_blocking_message_time("Camera position dumped!")

    def hsneo_dump_camera2(self):
        from Studio import Studio
        studio = Studio.Instance
        c = studio.cameraCtrl
        cdata = c.cameraData
        print("game.move_camera(pos=%s, distance=%s, rotate=%s)"%(str(cdata.pos), str(cdata.distance), str(cdata.rotate)))
        print("# for VN Scene Script %s"%self.camera_calcstr_for_vnscene())
        print ("# other one: 'cam': {'goto_pos': ((%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f))}, "%(cdata.pos.x, cdata.pos.y, cdata.pos.z, cdata.distance.x, cdata.distance.y, cdata.distance.z, cdata.rotate.x, cdata.rotate.y, cdata.rotate.z))

    def camera_calcstr_for_vnscene(self):
        st = 0

        if hasattr(self,"scLastRunnedState"):
            st = self.scLastRunnedState

        from Studio import Studio
        studio = Studio.Instance
        c = studio.cameraCtrl
        cdata = c.cameraData

        s1 = "%s,%s,%s,23.0"%(str(cdata.pos), str(cdata.distance), str(cdata.rotate))

        return "a:%s:camo:%s"%(str(st),s1.replace("(","").replace(")","").replace(" ",""))

    def dump_camera(self):
        self.hsneo_dump_camera()

    def dump_scene_vnframe(self):
        import vnframe
        import vnactor

        output = ""

        self.scenef_register_actorsprops()

        actors = self.scenef_get_all_actors()
        try:
            for id in actors:
                actor = self.scenef_get_actor(id)
                status = actor.export_full_status()
                output += ("'%s': " % id)+vnframe.script2string(status) + ",\n"
        except Exception as e:
            print "Error in status: ", e
            game.show_blocking_message_time("Error during dump actor %s!"%id)
            return

        props = self.scenef_get_all_props()
        try:
            for id in props:
                prop = self.scenef_get_propf(id)
                status = prop.export_full_status()
                output += ("'%s': " % id)+vnframe.script2string(status) + ",\n"
        except Exception as e:
            print "Error in status: ", e
            game.show_blocking_message_time("Error during dump prop %s!"%id)
            return

        output += "'sys': "+vnframe.script2string(vnactor.export_sys_status(self)) + ",\n"

        c = self.studio.cameraCtrl
        cdata = c.cameraData
        output += "'cam': {'goto_pos': ((%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f))}\n" % (
            cdata.pos.x, cdata.pos.y, cdata.pos.z, cdata.distance.x, cdata.distance.y, cdata.distance.z, cdata.rotate.x,
            cdata.rotate.y, cdata.rotate.z)

        output = "{\n"+output+"}\n"

        try:
            f = open('dumppython.txt', 'a+')
            #f.write("import vnframe\nvnframe.act({\n")
            f.write(output)
            #f.write("})\n")
            f.write("\n")
            f.close()
        except Exception as e:
            print e

        #self.scenef_clean_actorsprops()

        self.show_blocking_message_time("VNFrame Scene dumped!")

    def dump_selected_vnframe(self):
        import vnframe
        import vnactor

        output = ""

        try:
            fem = HSNeoOCI.create_from_selected()
            actor = fem.as_actor
            status = actor.export_full_status()
            output += vnframe.script2string({'selected': status}) + "\n"
        except Exception as e:
            print "Error in status: ", e
            game.show_blocking_message_time("Error during dump actor %s!"%id)
            return

        try:
            f = open('dumppython.txt', 'a+')
            #f.write("import vnframe\nvnframe.act({\n")
            f.write(output)
            #f.write("})\n")
            f.write("\n")
            f.close()
        except Exception as e:
            print e

        #self.scenef_clean_actorsprops()

        self.show_blocking_message_time("VNFrame selected dumped!")

    def get_camera_num(self, camnum):
        studio = self.studio
        si = studio.sceneInfo
        cdatas = si.cameraData

        if camnum == 0:  # 0 camera is current camera. It may be interested due to some reasons
            c = studio.cameraCtrl
            cdata = c.cameraData
        else:
            cdata = cdatas[camnum - 1]

        camobj = self.camparams2vec(cdata.pos,cdata.distance,cdata.rotate,cdata.parse)
        #print camobj
        return camobj

    def reset(self):
        self._unload_scene_before()
        self.studio.InitScene(False)
    
    @property
    def studio(self):
        from Studio import Studio
        studio = Studio.Instance
        """ :type : dummyneoclasses.Studio"""
        return studio

    @property
    def studio_scene(self):
        return self.studio.sceneInfo

    def hsneo_dump_scene(self):
        f = open('dumppython.txt', 'a+')
        import sys
        tmp = sys.stdout
        sys.stdout = f
        print("---DUMP! Scene----")
        self.hsneo_dump_scene2()
        print("")
        sys.stdout = tmp
        f.close()
        
    def hsneo_dump_scene2(self):
        #print("Dumping scene 1!")
        from Studio import OCIChar
        #si = self.studio_scene
        dobjctrl = self.studio.dicObjectCtrl
        #print("Dumping scene 2!")
        print("# we are not dumping objects because of number... but you can enable it in code of hsneo_dump_scene2")
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            #print(key)
            if isinstance(objctrl, OCIChar):
                print("objctrlchar = game.get_objctrl_num_tochar(%s) # char name %s, animid=%s"%(key, to_roman_file(objctrl.treeNodeObject.textName), to_roman_file(objctrl.charAnimeCtrl.name)))
                #print("objctrlchar = game.get_objctrl_num_tochar(%s) # char name" % (key))
                # objctrl.charAnimeCtrl.name
                pctrl = HSNeoOCIChar(objctrl)
                pctrl.dump_obj()
            else:
                #uncomment here to dump not only chars in scene
                #print("objctrl = game.get_objctrl_num(%s)"%(key))
                objctrl = objctrl
            #print key
        #print("Dumping scene End!")
        self.show_blocking_message_time("Scene dumped!")

    def dump_scene(self):
        if self.onDumpSceneOverride != None:
            self.onDumpSceneOverride(self)
        else:
            self.hsneo_dump_scene()

    def get_objctrl_num(self,num): # return ObjectCtrlInfo object from dicObjectCtrl
        #si = self.studio_scene
        #dobj = si.dicObject
        dobjctrl = self.studio.dicObjectCtrl
        return dobjctrl[num]
    
    def get_objctrl_num_tochar(self,num): # return HSNeoOCIChar by num
        return HSNeoOCIChar(self.get_objctrl_num(num))

    def scene_get_all_females(self):
        from Studio import OCIChar
        from Studio import OCICharFemale
        from Studio import OCICharMale
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCICharFemale):
                pctrl = HSNeoOCIChar(objctrl)
                ar.append(pctrl)
        return ar

    def scene_get_all_males(self):
        from Studio import OCIChar
        from Studio import OCICharFemale
        from Studio import OCICharMale
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCICharMale):
                pctrl = HSNeoOCIChar(objctrl)
                ar.append(pctrl)
        return ar

    def scene_get_all_items_raw(self):
        from Studio import OCIItem
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIItem):
                pctrl = objctrl
                ar.append(pctrl)
        return ar

    def scene_get_all_items(self):
        from Studio import OCIItem
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIItem):
                pctrl = objctrl
                ar.append(HSNeoOCIItem(pctrl))
        return ar

    def scene_get_all_folders_raw(self):
        from Studio import OCIFolder
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIFolder):
                pctrl = objctrl
                ar.append(pctrl)
        return ar

    def scene_get_all_folders(self):
        from Studio import OCIFolder
        ar = []
        dobjctrl = self.studio.dicObjectCtrl
        for key in dobjctrl.Keys:
            objctrl = dobjctrl[key]
            if isinstance(objctrl, OCIFolder):
                pctrl = objctrl
                ar.append(HSNeoOCIFolder(pctrl))
        return ar


    def scene_set_bg_png(self,filepng):
        #uiBGChanger = self.studio.uiBGChanger
        #uiBGChanger.ChangeBackgroundMode(True)
        #uiBGChanger.SetBackgroundNo(5)
        #ffile = self.get_scene_dir() + "\\"+self.sceneDir+filepng
        ffile = "..\\Studio\\scene\\" + self.sceneDir + filepng
        print self.studio.m_BackgroundCtrl.Load(ffile)
        """
        print ffile
        import PngAssist
        #sprite = PngAssist.LoadSpriteFromFile(ffile);
        #uiBGChanger.bgImage.sprite = sprite;
        #print "2"
        #
        texture = PngAssist.LoadTexture(ffile)
        material = self.studio.m_BackgroundCtrl.meshRenderer.material;
        material.SetTexture("_MainTex", texture);
        self.studio.m_BackgroundCtrl.meshRenderer.material = material;
        self.studio.m_BackgroundCtrl.isVisible = True
        print "2"
        """

    def vnscenescript_run_current(self,onEnd,startState = 0):
        #print "Run current!"
        module = import_or_reload("vnscenescript")
        self.run_menu(module.start_menu,{'mode': 'view', 'startState': startState},onEnd)

    def vnscenescript_run_filescene(self,file,onEnd):
        self.runScAct = onEnd
        self.load_scene(file)
        self.set_text_s("...")
        self.set_buttons_alt([])
        self.set_timer(0.5,self._vnscenescript_run_filescene)

    def _vnscenescript_run_filescene(self, game):
        self.set_timer(0.5, self._vnscenescript_run_filescene2)

    def _vnscenescript_run_filescene2(self,game):
        self.vnscenescript_run_current(self.runScAct)

    def scene_get_bg_png_orig(self):
        return self.studio.sceneInfo.background

    def scene_set_bg_png_orig(self, filepng):
        if self.scene_get_bg_png_orig() != filepng:
            # return self.studio.sceneInfo.background
            from Studio import BackgroundCtrl
            from UnityEngine import GameObject
            # print self.studio.m_BackgroundCtrl.Load(ffile)
            # for obj in GameObject.FindObjectOfType(BackgroundCtrl):
            obj = GameObject.FindObjectOfType(BackgroundCtrl)
            return obj.Load(filepng)
            # print self.studio.m_BackgroundCtrl.Load(ffile)
        return True

    # -------- scene with framework ------------
    def scenef_register_actorsprops(self):
        print "-- Framework: register actors and props start --"
        game = self
        # search for tag folder (-actor:,-prop:,-dlgFolder-) and load them into game automaticlly
        # so this function must be called AFTER SCENE HAD BE LOADED!!

        game._scenef_actors = {}
        game._scenef_props = {}

        # get all from scene
        folders = game.scene_get_all_folders_raw()  # get list<OCIFolder> of folders in scene,

        # load actors and props from -actor:/-prop: tag folder attach on char/item
        for fld in folders:
            ftn = fld.name
            if ftn.startswith("-actor:"):
                # analysis actor tag
                tagElements = ftn[7:].strip().split(":")
                if len(tagElements) == 1:
                    actorAlias = tagElements[0]
                    # actorColor = "ffffff"
                    actorColor = None
                    actorTitle = None
                elif len(tagElements) == 2:
                    actorAlias = tagElements[0]
                    actorColor = tagElements[1]
                    actorTitle = None
                else:
                    actorAlias = tagElements[0]
                    actorColor = tagElements[1]
                    actorTitle = tagElements[2]
                # register actor
                try:
                    hsociChar = HSNeoOCI.create_from_treenode(fld.treeNodeObject.parent.parent.parent)
                    if isinstance(hsociChar, HSNeoOCIChar):
                        from vnactor import *
                        if actorTitle == None: actorTitle = hsociChar.text_name

                        #game._scenef_actors[actorAlias] = Actor(hsociChar.objctrl)
                        #adapted to multiple frameworks in 2.0
                        game._scenef_actors[actorAlias] = hsociChar.as_actor

                        if actorColor != None:
                            game.register_char(actorAlias, actorColor, actorTitle)
                        print "Registered actor: '" + to_roman(actorAlias) + "' as " + to_roman(actorTitle) + " (#" + str(actorColor) + ")"
                    else:
                        print "Error in register char tag (not correct child) <" + to_roman(ftn) + ">"
                except Exception as e:
                    print "error in register char tag <" + to_roman(ftn) + ">: " + str(e)
                    continue
            elif ftn.startswith("-prop:"):
                # analysis props tag
                propAlias = ftn[6:].strip()
                # register props
                hsobj = HSNeoOCI.create_from_treenode(fld.treeNodeObject.parent)
                game._scenef_props[propAlias] = hsobj
                print "Registered prop: '" + to_roman(propAlias) + "' as " + to_roman(hsobj.text_name)
            elif ftn.startswith("-propchild:"):
                # analysis props tag
                propAlias = ftn[11:].strip()
                # register props
                hsobj = HSNeoOCI.create_from_treenode(fld.treeNodeObject.child[0])
                game._scenef_props[propAlias] = hsobj
                print "Registered prop: '" + to_roman(propAlias) + "' as " + to_roman(hsobj.text_name)


        print "-- Framework: register actors and props end --"

    def scenef_get_all_actors(self):
        return self._scenef_actors

    def scenef_get_all_props(self):
        return self._scenef_props

    def scenef_get_prop(self,id):
        if id in self.scenef_get_all_props():
            obj = self.scenef_get_all_props()[id]
            """ :type : HSNeoOCI"""
            return obj
        return None

    def scenef_get_propf(self,id):
        if id in self.scenef_get_all_props():
            from vnactor import *
            obj = self.scenef_get_all_props()[id]
            """ :type : HSNeoOCIProp"""

            return obj.as_prop
        return None

    def scenef_get_actor(self,id):
        if id in self.scenef_get_all_actors():
            obj = self.scenef_get_all_actors()[id]
            """ :type : vnactor.Actor"""
            return obj
        return None

    def scenef_reg_actor(self,id,actor):
        self._scenef_actors[id] = actor

    def scenef_reg_prop(self,id,prop):
        self._scenef_props[id] = prop

    def scenef_clean_actorsprops(self):
        self._scenef_actors = {}
        self._scenef_props = {}

    # ---- lip sync -------
    def set_text(self, char, text):
        VNController.set_text(self,char,text)
        if self.isfAutoLipSync:
            try:
                self._flipsync_text_handler(char,text)
            except Exception, e:
                print "Error in flipsync: "+str(e)

    def _flipsync_text_handler(self, char, text):
        import vngelipsync
        vngelipsync.flipsync_text_handler(self,char,text)

    def fake_lipsync_stop(self):
        import vngelipsync
        vngelipsync.fake_lipsync_stop(self)

    # --------- sync_h ---------
    def sync_h(self,female,male):
        factor = female.as_actor
        """ :type : vnactor.Actor"""
        mactor = male.as_actor
        """ :type : vnactor.Actor"""

        anime_option_param = (factor.height, factor.breast)

        # if factor.isHAnime:
        factor.set_anime_option_param(anime_option_param)
        # if mactor.isHAnime:
        mactor.set_anime_option_param(anime_option_param)


def vngame_window_neo(vnButtonsStart, vnButtonsActionsStart):
    import unity_util

    unity_util.clean_behaviors()
    class NeoController(VNNeoController):
        def __init__(self):
            self.engine_name = "neo"
            self.pygamepath = "Plugins\\Console\\Lib"
            self._vnButtons = vnButtonsStart
            self._vnButtonsActions = vnButtonsActionsStart
            VNNeoController.__init__(self)
    
        # --- support functions ----
        def load_scene(self,file):
            self._load_scene_before(file)

            self.funcLockedText = "Loading scene..."
            self.isFuncLocked = True
            
            self.updFuncParam = file
            self.updFunc = self.load_scene2 # load scene on after one Update cycle
            
        def load_scene2(self,file):
            self.updFunc = self.load_scene_immediately # load scene on next Update cycle
        
        def load_scene_immediately(self,file):
            from Studio import Studio
            studio = Studio.Instance
            from os import path
            #return path.join(get_scene_dir(),file)
            studio.LoadScene(path.join(self.get_scene_dir(),self.sceneDir+file))
            
            self.isFuncLocked = False

        def get_scene_dir(self):
            from UnityEngine import Application
            from os import path
            return path.realpath(path.join(Application.dataPath,'..','UserData','studioneo','scene'))



        def scene_set_bg_png(self, filepng):
            ffile = "..\\studioneo\\scene\\" + self.sceneDir + filepng
            #print self.studio.m_BackgroundCtrl.Load(ffile)
            self.scene_set_bg_png_orig(ffile)

        # def Update(self):
        #     VNNeoController.Update(self)
        #     print "Upd!"

        # def OnDestroy(self):
        #     print "OnDestroy NEO!"
        #     import coroutine
        #     coroutine.start_new_coroutine(vngame_window_autogames_uni, (), None)


    game = unity_util.create_gui_behavior(NeoController)
    return game



def vngame_window_phstudio(vnButtonsStart, vnButtonsActionsStart):
    import vngameenginephstudio
    game = vngameenginephstudio.vngame_window_phstudio(vnButtonsStart, vnButtonsActionsStart)
    return game

def vngame_window_charastudio(vnButtonsStart, vnButtonsActionsStart):
    import unity_util

    unity_util.clean_behaviors()
    class CharaStudioController(VNNeoController):
        def __init__(self):
            self.engine_name = "charastudio"
            self.pygamepath = self.calc_py_path() #"BepInEx\\Console\\Lib"
            self._vnButtons = vnButtonsStart
            self._vnButtonsActions = vnButtonsActionsStart
            VNNeoController.__init__(self)
            
        # --- support functions ----
        
        def load_scene(self,file):
            self._load_scene_before(file)

            from Studio import Studio
            studio = Studio.Instance
            self.change_map_to(-1) # unload map before loading - due to stability issues
            #studio.InitScene(False) # or init scene to false
            self.updFuncParam = file
            self.updFunc = self.load_scene2 # load scene on next Update cycle
            
            self.funcLockedText = "Loading scene..."
            self.isFuncLocked = True
            
            #self.saveTChar = 
            
        def load_scene2(self,file):
            self.updFunc = self.load_scene3 # load scene on next Update cycle
            
        def load_scene3(self,file):
            self.updFunc = self.load_scene_immediately # load scene on next Update cycle
            
        def load_scene_immediately(self,file):
            from Studio import Studio
            studio = Studio.Instance
            from os import path
            #return path.join(get_scene_dir(),file)
            
            fpath = path.join(self.get_scene_dir(),self.sceneDir+file)
            studio.LoadScene(fpath) # not always loading map. Can't say, what to do. So we must unload map before LoadScene
            #self.change_map_to(-1)
            #self.change_map_to(studio.sceneInfo.map)
            
            #self.updFunc = self.testupdfunc
            
            # -------- this loading scene certainly work, but show scene unimmersive, step-by-step -----------
            #studio.StartCoroutine(studio.LoadSceneCoroutine(fpath))
            self.isFuncLocked = False
    
        def get_scene_dir(self):
            from UnityEngine import Application
            from os import path
            return path.realpath(path.join(Application.dataPath,'..','UserData','Studio','scene'))        
            
        def change_map_to(self,mapnum):
            from Studio import Studio
            studio = Studio.Instance
            studio.AddMap(mapnum,True,False,False)

        def scene_set_bg_png(self, filepng):
            """
            try:
                ffile = "..\\studio\\scene\\" + self.sceneDir + filepng
                print ffile
                from Studio import BackgroundCtrl
                from UnityEngine import GameObject
                #print self.studio.m_BackgroundCtrl.Load(ffile)
                #for obj in GameObject.FindObjectOfType(BackgroundCtrl):
                obj = GameObject.FindObjectOfType(BackgroundCtrl)
                print obj.Load(ffile)
            except Exception, e:
                print("Error: " + str(e))
            """
            ffile = "..\\studio\\scene\\" + self.sceneDir + filepng
            self.scene_set_bg_png_orig(ffile)

        def scene_get_framefile(self):
            return self.studio_scene.frame

        def scene_set_framefile(self,ffile):
            from Studio import FrameCtrl
            from UnityEngine import GameObject
            obj = GameObject.FindObjectOfType(FrameCtrl)
            return obj.Load(ffile)

    game = unity_util.create_gui_behavior(CharaStudioController)
    return game


def vngame_window_neov2(vnButtonsStart, vnButtonsActionsStart):
    import unity_util

    unity_util.clean_behaviors()

    class NeoV2Controller(VNNeoController):
        def __init__(self):
            self.engine_name = "neov2"
            self.pygamepath = self.calc_py_path() # "Bepinex\\plugins\\Console\\Lib"
            self._vnButtons = vnButtonsStart
            self._vnButtonsActions = vnButtonsActionsStart
            VNNeoController.__init__(self)

        # --- support functions ----
        def load_scene(self, file):
            self._load_scene_before(file)

            self.funcLockedText = "Loading scene..."
            self.isFuncLocked = True

            self.updFuncParam = file
            self.updFunc = self.load_scene2  # load scene on after one Update cycle

        def load_scene2(self, file):
            self.updFunc = self.load_scene_immediately  # load scene on next Update cycle

        def load_scene_immediately(self, file):
            from Studio import Studio
            studio = Studio.Instance
            from os import path
            # return path.join(get_scene_dir(),file)
            studio.LoadScene(path.join(self.get_scene_dir(), self.sceneDir + file))

            self.isFuncLocked = False

        def get_scene_dir(self):
            from UnityEngine import Application
            from os import path
            return path.realpath(path.join(Application.dataPath, '..', 'UserData', 'Studio', 'scene'))

        def scene_set_bg_png(self, filepng):
            ffile = "..\\studio\\scene\\" + self.sceneDir + filepng
            # print self.studio.m_BackgroundCtrl.Load(ffile)
            self.scene_set_bg_png_orig(ffile)

        def scene_get_framefile(self):
            return self.studio_scene.frame

        def scene_set_framefile(self,ffile):
            from Studio import FrameCtrl
            from UnityEngine import GameObject
            obj = GameObject.FindObjectOfType(FrameCtrl)
            return obj.Load(ffile)



    game = unity_util.create_gui_behavior(NeoV2Controller)
    return game

# ---------------- --- dumping item tree -----------------

def dump_selected_item_tree():
    item = HSNeoOCI.create_from_selected()
    f = open("dump_selected_items.txt", 'w')
    _dump_item_tree(f,item,0)
    f.close()

def _dump_item_tree(f,item,level):
    txt1 = ""
    if isinstance(item,HSNeoOCIItem):
        addparams = "";
        try:
            tmp = (", 'anSp': {0}").format(item.objctrl.animeSpeed)
            addparams += tmp
        except Exception:
            pass
        txt1 = "{"+"'no': {0}, 'prs': ({1},{2},{3})".format(item.no,item.pos,item.rot,item.scale)
        txt1 += addparams
    if isinstance(item, HSNeoOCIFolder):
        value = item.name
        if not only_roman_chars(value):
            value = "nonlatinname"
        txt1 = "{" + "'no': 'fold', 'name': '{2}', 'pr': ({0},{1})".format(item.pos, item.rot, value)

    if item.treeNodeObject.childCount > 0:
        txt1 += ", 'ch': ["
        _print_dump(f,txt1,level)

        # print all child
        for childt in item.treeNodeObject.child:
            child = HSNeoOCI.create_from_treenode(childt)
            _dump_item_tree(f,child,level+1)

        _print_dump(f,"]},",level)
    else:
        _print_dump(f, txt1+"}", level)

def _print_dump(f,txt,level):
    #print(" "*level*4+txt)
    f.write(" "*level*4+txt+"\n")

def load_item_tree(obj,itemparenttobj):
    if isinstance(obj["no"],str):
        item = HSNeoOCIFolder.add(obj["name"])
        if itemparenttobj != None:
            item.set_parent_treenodeobject(itemparenttobj)
        item.set_pos(obj["pr"][0])
        item.set_rot(obj["pr"][1])
    else:
        item = HSNeoOCIItem.add_item(obj["no"])
        if itemparenttobj != None:
            item.set_parent_treenodeobject(itemparenttobj)
        item.set_pos(obj["prs"][0])
        item.set_rot(obj["prs"][1])
        item.set_scale(obj["prs"][2])
        if 'anSp' in obj:
            item.objctrl.animeSpeed = float(obj["anSp"])

    if 'ch' in obj:
        for objch in obj["ch"]:
            load_item_tree(objch,item.treeNodeObject)

    return item

def only_roman_chars(s):
    try:
        s.encode("ascii")
        return True
    except Exception, e:
        return False

def to_roman(s):
    # potentially convert all symbols to latin
    # but now only return s
    # this is used in console, so we don't always convert it to latin
    return s

def to_roman_file(s):
    # when we write to file, we want always be latin
    if only_roman_chars(s):
        return s
    return "nonlatin"

import random
rng=random.WichmannHill()
# to correct distribution
# see https://github.com/IronLanguages/ironpython2/issues/231 for details

def random_randint(a,b):
    # seems to be an error in IronPython random.randint, so we use our realization
    #import random
    res = rng.randint(a,b+1)
    if res > b:
        return rng.randint(a,b)
    return res

def random_choice(ar):
    try:
        item = random_randint(0,len(ar)-1)
        #print "random_choice: ",item
        res = ar[item]
        return res
    except Exception, e:
        print "Error in random_choice, %s " % str(e)
        pass
    return ar[0]

def CloneSkin(skin):
    newskin = GUISkin()
    props = newskin.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance| BindingFlags.SetProperty)
    for prop in [x.Name for x in props]:
        try:
            setattr(newskin, prop, getattr(skin, prop))
        except:
            pass
    return newskin

def console_show(visible):
    import ctypes
    # crt_win_hwid = ctypes.windll.user32.FindWindowA(0, 'Unity Console')
    crt_win_hwid = ctypes.windll.kernel32.GetConsoleWindow()
    #game.show_blocking_message_time(str(crt_win_hwid))
    if visible:
        ctypes.windll.user32.ShowWindow(crt_win_hwid, 1)
    else:
        ctypes.windll.user32.ShowWindow(crt_win_hwid, 0)

# -- studio_wait_for_load trick - for NEO and Old Studio--
def studio_wait_for_load():
    parseIniFile()
    option = getEngineOptions()
    if "hideconsoleafterstart" in option:
        if option["hideconsoleafterstart"] == "1":
            console_show(False)

    if get_engine_id() == "":
        print "VN Game Engine not for this EXE file"
        return
    from UnityEngine.SceneManagement import SceneManager
    #print "SceneManager: ",SceneManager.GetActiveScene().name
    from System.Threading import Thread

    # we wait for 300 seconds before scene will set to Studio - and run after that
    for i in range(0,300):
        Thread.Sleep(1000)


        #print "!!"
        #print "SceneManager: ", i, SceneManager.GetActiveScene().name
        if SceneManager.GetActiveScene().name.lower() == "studio":
            vngame_window_autogames_uni_1init()
            return

    print "Studio loads more than 300 seconds... seems to be an error."
    return
    #yield WaitForSeconds(8)
    #print "SceneManager3: ", i,  SceneManager.GetActiveScene().name
    #import coroutine
    #coroutine.start_new_coroutine(neo_preload2, (), None)

# -- studio_wait_for_load trick - for NEO and Old Studio--
# def studio_wait_for_load2():
#     if get_engine_id() == "":
#         print "VN Game Engine not for this EXE file"
#         return
#     from UnityEngine.SceneManagement import SceneManager
#     #print "SceneManager: ",SceneManager.GetActiveScene().name
#     from System.Threading import Thread
#
#     # we wait for 300 seconds before scene will set to Studio - and run after that
#     for i in range(0,300):
#         Thread.Sleep(1000)
#
#
#         #print "!!"
#         #print "SceneManager: ", i, SceneManager.GetActiveScene().name
#         if SceneManager.GetActiveScene().name.lower() == "studio":
#             vngame_window_autogames_uni_1init()
#             return
#
#     print "Studio loads more than 300 seconds... seems to be an error."
#     return
#     #yield WaitForSeconds(8)
#     #print "SceneManager3: ", i,  SceneManager.GetActiveScene().name
#     #import coroutine
#     #coroutine.start_new_coroutine(neo_preload2, (), None)
