├── .gitignore ├── LICENSE ├── README.md ├── haxelib.json └── kala ├── DrawingData.hx ├── EventHandle.hx ├── Kala.hx ├── asset ├── AssetType.hx ├── Assets.hx ├── Builder.hx ├── Loader.hx └── sheet │ ├── SheetData.hx │ └── TexturePacker.hx ├── audio ├── Audio.hx ├── AudioChannel.hx └── AudioGroup.hx ├── behaviors ├── Behavior.hx ├── Timer.hx ├── collision │ ├── BaseCollider.hx │ ├── BaseCollisionShape.hx │ ├── basic │ │ ├── Collider.hx │ │ └── shapes │ │ │ ├── CollisionCircle.hx │ │ │ ├── CollisionRectangle.hx │ │ │ ├── CollisionShape.hx │ │ │ └── ShapeType.hx │ └── transformable │ │ ├── Collider.hx │ │ ├── CollisionResult.hx │ │ └── shapes │ │ ├── CollisionCircle.hx │ │ ├── CollisionPolygon.hx │ │ └── CollisionShape.hx ├── display │ ├── Clip.hx │ ├── Flicker.hx │ └── SpriteAnimation.hx ├── input │ ├── BaseButtonInteraction.hx │ ├── BasicButtonInteraction.hx │ ├── ButtonInteraction.hx │ └── MouseInteraction.hx ├── motion │ └── VelocityMotion.hx └── tween │ ├── Ease.hx │ └── Tween.hx ├── debug ├── Console.hx ├── Debug.hx ├── Debugger.hx └── Tab.hx ├── graphics └── Shader.hx ├── input ├── ButtonInputHandle.hx ├── Keyboard.hx ├── Mouse.hx └── Touch.hx ├── lang ├── Builder.hx └── Lang.hx ├── math ├── Collision.hx ├── Mathf.hx ├── Matrix.hx ├── Position.hx ├── Random.hx ├── Rect.hx ├── Rotation.hx ├── Vec2.hx ├── Vec2T.hx ├── Velocity.hx ├── color │ ├── BlendMode.hx │ └── Color.hx └── shapes │ └── Triangle.hx ├── objects ├── Object.hx ├── group │ ├── Group.hx │ └── View.hx ├── shapes │ ├── Circle.hx │ ├── Polygon.hx │ ├── Rectangle.hx │ └── Shape.hx ├── sprite │ ├── BaseButtonSprite.hx │ ├── BasicButtonSprite.hx │ ├── ButtonSprite.hx │ └── Sprite.hx └── text │ ├── BasicText.hx │ ├── BitmapFont.hx │ ├── Font.hx │ ├── Text.hx │ └── TextAlign.hx ├── system └── HTML5.hx └── util ├── Axes.hx ├── StringUtil.hx ├── pool └── Pool.hx └── types ├── Pair.hx └── Trio.hx /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nguyen Phuc Sinh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kala is a component / object-behavior based 2D game engine powered by [Kha](https://github.com/KTXSoftware/Kha). The project is still a proof of concept but can be expected to grow a lot in the future. 2 | 3 | Games made with Kala: 4 | 5 | - [bioDigit (HTML5 version)](https://github.com/melon-not-found/bioDigit-Kala) 6 | -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kala", 3 | "url" : "", 4 | "license": "MIT", 5 | "tags": ["kha", "game", "cross", "2d"], 6 | "description": "Kala is a 2D game engine powered by Kha.", 7 | "version": "0.4", 8 | "releasenote": "", 9 | "contributors": ["hazame"], 10 | "dependencies": { "kha": "" } 11 | } 12 | -------------------------------------------------------------------------------- /kala/DrawingData.hx: -------------------------------------------------------------------------------- 1 | package kala; 2 | 3 | import kala.math.color.BlendMode; 4 | import kala.math.color.Color; 5 | import kala.math.Matrix; 6 | import kha.FastFloat; 7 | 8 | class DrawingData { 9 | 10 | public var antialiasing:Bool; 11 | public var transformation:Matrix; 12 | 13 | public var color:Null; 14 | public var colorBlendMode:BlendMode; 15 | public var colorAlphaBlendMode:BlendMode; 16 | 17 | public var opacity:FastFloat; 18 | 19 | /** 20 | * Used to send data from groups to their members throught post draw callback. 21 | */ 22 | public var extra:Dynamic; 23 | 24 | public inline function new( 25 | antialiasing:Bool, 26 | transformation:Matrix, 27 | color:Null, 28 | colorBlendMode:BlendMode, ?colorAlphaBlendMode:BlendMode, 29 | opacity:FastFloat, 30 | extra:Dynamic 31 | ) { 32 | this.antialiasing = antialiasing; 33 | this.transformation = transformation; 34 | this.color = color; 35 | this.colorBlendMode = colorBlendMode; 36 | this.colorAlphaBlendMode = colorAlphaBlendMode; 37 | this.opacity = opacity; 38 | this.extra = extra; 39 | } 40 | 41 | @:extern 42 | public inline function clone():DrawingData { 43 | return new DrawingData( 44 | antialiasing, 45 | transformation, 46 | color, 47 | colorBlendMode, colorAlphaBlendMode, 48 | opacity, 49 | extra 50 | ); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /kala/EventHandle.hx: -------------------------------------------------------------------------------- 1 | package kala; 2 | import kala.behaviors.Behavior; 3 | 4 | import kala.behaviors.Behavior.IBehavior; 5 | 6 | class EventHandle { 7 | 8 | private var _cbHandles:Array = new Array(); 9 | 10 | public function new() { 11 | 12 | } 13 | 14 | public function clearCBHandles():Void { 15 | for (handle in _cbHandles) handle.removeAll(); 16 | } 17 | 18 | inline function destroyCBHandles():Void { 19 | while (_cbHandles.length > 0) { 20 | _cbHandles.pop().destroy(); 21 | } 22 | 23 | _cbHandles = null; 24 | } 25 | 26 | 27 | inline function addCBHandle(handle:T):T { 28 | _cbHandles.push(handle); 29 | return handle; 30 | } 31 | 32 | } 33 | 34 | @:allow(kala.EventHandle) 35 | interface ICallbackHandle { 36 | private function removeAll():Void; 37 | private function destroy():Void; 38 | } 39 | 40 | @:allow(kala.behaviors.Behavior) 41 | class CallbackHandle implements ICallbackHandle { 42 | 43 | public var count(get, never):Int; 44 | 45 | private var _callbacks:Array> = new Array>(); 46 | 47 | public function new() { 48 | 49 | } 50 | 51 | public inline function iterator():Iterator> { 52 | return _callbacks.iterator(); 53 | } 54 | 55 | public inline function notify(callback:T):Void { 56 | _callbacks.push(new Callback(callback)); 57 | } 58 | 59 | /** 60 | * Remove callback from this handle if it wasn't added by a behavior. 61 | * 62 | * @param callback The callback to be removed. 63 | */ 64 | public function remove(callback:T):Void { 65 | var i = 0; 66 | for (cb in _callbacks) { 67 | if (cb.cbFunction == callback && cb.owner == null) { 68 | _callbacks.splice(i, 1); 69 | return; 70 | } 71 | i++; 72 | } 73 | } 74 | 75 | inline function notifyPrivateCB(owner:Dynamic, callback:T):Void { 76 | _callbacks.push(new Callback(callback, owner)); 77 | } 78 | 79 | function removePrivateCB(owner:Dynamic, callback:T):Void { 80 | var i = 0; 81 | for (cb in _callbacks) { 82 | if (cb.cbFunction == callback && cb.owner == owner) { 83 | _callbacks.splice(i, 1); 84 | return; 85 | } 86 | i++; 87 | } 88 | 89 | throw 'Incorrectly tried to remove a private callback of $owner from object $this.'; 90 | } 91 | 92 | function removeAll():Void { 93 | _callbacks.splice(0, _callbacks.length); 94 | } 95 | 96 | function destroy():Void { 97 | _callbacks = null; 98 | } 99 | 100 | function get_count():Int { 101 | return _callbacks.length; 102 | } 103 | 104 | } 105 | 106 | class Callback { 107 | 108 | public var cbFunction(default, null):T; 109 | public var owner(default, null):Dynamic; 110 | 111 | public inline function new(cbFunction:T, behavior:Dynamic = null) { 112 | this.cbFunction = cbFunction; 113 | this.owner = behavior; 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /kala/Kala.hx: -------------------------------------------------------------------------------- 1 | package kala; 2 | 3 | import kala.asset.Assets; 4 | import kala.audio.Audio; 5 | import kala.debug.Debug; 6 | import kala.input.Keyboard; 7 | import kala.input.Mouse; 8 | import kala.input.Touch; 9 | import kala.math.color.Color; 10 | import kala.objects.group.View; 11 | import kala.objects.Object; 12 | import kala.objects.group.Group; 13 | import kha.FastFloat; 14 | import kha.Font; 15 | import kha.Framebuffer; 16 | import kha.Scheduler; 17 | import kha.System; 18 | import kha.SystemImpl; 19 | 20 | class Kala { 21 | 22 | /** 23 | * Root group. 24 | */ 25 | public static var world(default, null):GenericGroup = new GenericGroup(false); 26 | 27 | /** 28 | * How many times the game will be updated per second. 29 | */ 30 | public static var updateRate(default, set):UInt; 31 | 32 | /** 33 | * How many times the game will be rendered per second. 34 | * READ-ONLY 35 | */ 36 | public static var drawRate(get, null):Int; 37 | 38 | public static var fps(default, null):UInt; 39 | 40 | /** 41 | * Delta value when the game runs in perfect framerate. 42 | */ 43 | public static var perfectDelta:FastFloat; 44 | 45 | /** 46 | * Elapsed time of two last successive frames in seconds. 47 | */ 48 | public static var delta:FastFloat = 0; 49 | 50 | /** 51 | * If true, use seconds for calculating timestep otherwise use frames. 52 | * Timestep is still affected by timeScale when this set to false. 53 | * 54 | * DEFAULT: false 55 | */ 56 | public static var deltaTiming:Bool = false; 57 | 58 | /** 59 | * Scale factor to calculate elapsed time. This affects all built-in timing processes of objects and behaviors. 60 | */ 61 | public static var timeScale:FastFloat = 1; 62 | 63 | /** 64 | * The current screen width. 65 | */ 66 | public static var width(default, null):Int = 0; 67 | 68 | /** 69 | * The current screen height. 70 | */ 71 | public static var height(default, null):Int = 0; 72 | 73 | /** 74 | * Background color. 75 | */ 76 | public static var bgColor:Color = 0xff000000; 77 | 78 | /** 79 | * Default font used for text rendering. 80 | */ 81 | public static var defaultFont(default, set):Font; 82 | 83 | 84 | /** 85 | * Default view used to calculate mouse & touch position. 86 | */ 87 | public static var defaultView:View; 88 | 89 | // 90 | 91 | #if js 92 | public static var html5(default, null):kala.system.HTML5; 93 | #end 94 | 95 | // 96 | 97 | private static var _updateTaskID:Null; 98 | private static var _prvUpdateTime:FastFloat = 0; 99 | 100 | /** 101 | * Start the game. 102 | * 103 | * @param title The windows title. 104 | * @param width The width of the game window / screen. 105 | * @param height The height of the game window / screen. 106 | * @param updateRate How many times the game will be updated per second. 107 | * @param loadAllAssets If true, load all assets. 108 | */ 109 | public static function start( 110 | ?title:String = "Hello!", 111 | ?screenWidth:UInt = 800, ?screenHeight:UInt = 600, 112 | ?antiAliasingSamples:UInt = 1, 113 | ?updateRate:UInt = 60, 114 | ?loadAllAssets:Bool = true 115 | ):Void { 116 | width = screenWidth; 117 | height = screenHeight; 118 | 119 | System.init( 120 | { 121 | title: title, 122 | width: width, 123 | height: height, 124 | samplesPerPixel: antiAliasingSamples 125 | }, 126 | function() { 127 | #if js 128 | html5 = new kala.system.HTML5(); 129 | #end 130 | 131 | if (loadAllAssets) { 132 | Assets.loadEverything( 133 | function() startWorld(updateRate) 134 | ); 135 | } else startWorld(updateRate); 136 | } 137 | ); 138 | } 139 | 140 | /** 141 | * Return a new value relative to the current game framerate. 142 | */ 143 | public static inline function applyDelta(value:FastFloat):FastFloat { 144 | return delta / perfectDelta * value; 145 | } 146 | 147 | 148 | public static inline function openURL(url:String, ?target:String = "_blank"):Void { 149 | #if js 150 | js.Browser.window.open(url, target); 151 | #elseif flash 152 | flash.Lib.getURL(new flash.net.URLRequest(url), target); 153 | #else 154 | System.loadUrl(url); 155 | #end 156 | } 157 | 158 | public static inline function requestFullscreen():Void { 159 | SystemImpl.requestFullscreen(); 160 | } 161 | 162 | static function startWorld(updateRate:UInt):Void { 163 | #if (debug || kala_debug || kala_keyboard) 164 | Keyboard.init(); 165 | #end 166 | 167 | #if (debug || kala_debug || kala_mouse) 168 | Mouse.init(); 169 | #end 170 | 171 | #if kala_touch 172 | Touch.init(); 173 | #end 174 | 175 | System.notifyOnRender(renderWorld); 176 | Kala.updateRate = updateRate; 177 | } 178 | 179 | static function renderWorld(framebuffer:Framebuffer):Void { 180 | width = framebuffer.width; 181 | height = framebuffer.height; 182 | 183 | framebuffer.g2.begin(true, bgColor); 184 | 185 | world.callDraw(new DrawingData(false, null, null, null, null, 1, null), framebuffer); 186 | 187 | #if (debug || kala_debug) 188 | Debug.draw(framebuffer); 189 | #end 190 | 191 | framebuffer.g2.end(); 192 | } 193 | 194 | static function updateWorld():Void { 195 | var time = Scheduler.time(); 196 | delta = time - _prvUpdateTime; 197 | _prvUpdateTime = time; 198 | 199 | fps = Math.round(1 / delta); 200 | 201 | var elapsed = deltaTiming ? delta * timeScale : timeScale; 202 | 203 | #if (debug || kala_debug || kala_keyboard) 204 | Keyboard.update(elapsed); 205 | #end 206 | 207 | #if (debug || kala_debug || kala_mouse) 208 | Mouse.update(elapsed); 209 | #end 210 | 211 | #if kala_touch 212 | Touch.update(elapsed); 213 | #end 214 | 215 | world.callUpdate(elapsed); 216 | 217 | Audio.update(); 218 | } 219 | 220 | static function set_updateRate(value:UInt):UInt { 221 | perfectDelta = 1 / value; 222 | 223 | if (_updateTaskID != null) { 224 | Scheduler.removeTimeTask(_updateTaskID); 225 | } 226 | 227 | _updateTaskID = Scheduler.addTimeTask(updateWorld, 0, 1 / value); 228 | 229 | return updateRate = value; 230 | } 231 | 232 | static inline function get_drawRate():UInt { 233 | return System.refreshRate; 234 | } 235 | 236 | static function set_defaultFont(value:Font):Font { 237 | if (value == null) return null; 238 | return defaultFont = value; 239 | } 240 | 241 | } -------------------------------------------------------------------------------- /kala/asset/AssetType.hx: -------------------------------------------------------------------------------- 1 | package kala.asset; 2 | 3 | enum AssetType { 4 | 5 | IMAGE; 6 | SOUND; 7 | FONT; 8 | VIDEO; 9 | BLOB; 10 | 11 | } -------------------------------------------------------------------------------- /kala/asset/Assets.hx: -------------------------------------------------------------------------------- 1 | package kala.asset; 2 | 3 | import kala.asset.sheet.SheetData; 4 | import kha.Assets.BlobList; 5 | import kha.Assets.FontList; 6 | import kha.Assets.ImageList; 7 | import kha.Assets.SoundList; 8 | import kha.Assets.VideoList; 9 | import kha.Blob; 10 | import kha.Font; 11 | import kha.Image; 12 | import kha.Sound; 13 | import kha.Video; 14 | 15 | @:build(kala.asset.Builder.build("Sheet")) 16 | class SheetList { public function new() {} } 17 | 18 | @:build(kala.asset.Builder.build("FileArray")) 19 | class Assets { 20 | 21 | public static var loader(default, never):Loader = new Loader(); 22 | 23 | public static var sheets(default, never):SheetList = new SheetList(); 24 | 25 | // Below are wrappers for kha.Asset. 26 | 27 | public static var images(get, never):ImageList; 28 | public static var sounds(get, never):SoundList; 29 | public static var blobs(get, never):BlobList; 30 | public static var fonts(get, never):FontList; 31 | public static var videos(get, never):VideoList; 32 | 33 | public static var imageFormats(get, never):Array; 34 | public static var soundFormats(get, never):Array; 35 | public static var fontFormats(get, never):Array; 36 | public static var videoFormats(get, never):Array; 37 | 38 | public static inline function loadEverything(callback:Void->Void):Void { 39 | kha.Assets.loadEverything(callback); 40 | } 41 | 42 | // 43 | 44 | public static inline function loadImage(name:String, done:Image->Void):Void { 45 | kha.Assets.loadImage(name, done); 46 | } 47 | 48 | public static inline function loadImageFromPath(path:String, readable:Bool, done:Image->Void):Void { 49 | kha.Assets.loadImageFromPath(path, readable, done); 50 | } 51 | 52 | // 53 | 54 | public static inline function loadBlob(name:String, done:Blob->Void):Void { 55 | kha.Assets.loadBlob(name, done); 56 | } 57 | 58 | public static inline function loadBlobFromPath(path:String, done:Blob->Void):Void { 59 | kha.Assets.loadBlobFromPath(path, done); 60 | } 61 | 62 | // 63 | 64 | public static inline function loadSound(name:String, done:Sound->Void):Void { 65 | kha.Assets.loadSound(name, done); 66 | } 67 | 68 | public static inline function loadSoundFromPath(path:String, done:Sound->Void):Void { 69 | kha.Assets.loadSoundFromPath(path, done); 70 | } 71 | 72 | // 73 | 74 | public static inline function loadFont(name:String, done:Font->Void):Void { 75 | kha.Assets.loadFont(name, done); 76 | } 77 | 78 | public static inline function loadFontFromPath(path:String, done:Font->Void):Void { 79 | kha.Assets.loadFontFromPath(path, done); 80 | } 81 | 82 | // 83 | 84 | public static inline function loadVideo(name:String, done:Video->Void):Void { 85 | kha.Assets.loadVideo(name, done); 86 | } 87 | 88 | public static inline function loadVideoFromPath(path:String, done:Video->Void):Void { 89 | kha.Assets.loadVideoFromPath(path, done); 90 | } 91 | 92 | // 93 | 94 | static inline function get_images():ImageList { 95 | return kha.Assets.images; 96 | } 97 | 98 | static inline function get_sounds():SoundList { 99 | return kha.Assets.sounds; 100 | } 101 | 102 | static inline function get_blobs():BlobList { 103 | return kha.Assets.blobs; 104 | } 105 | 106 | static inline function get_fonts():FontList { 107 | return kha.Assets.fonts; 108 | } 109 | 110 | static inline function get_videos():VideoList { 111 | return kha.Assets.videos; 112 | } 113 | 114 | // 115 | 116 | static inline function get_imageFormats():Array { 117 | return kha.Assets.imageFormats; 118 | } 119 | 120 | static inline function get_soundFormats():Array { 121 | return kha.Assets.soundFormats; 122 | } 123 | 124 | static inline function get_fontFormats():Array { 125 | return kha.Assets.fontFormats; 126 | } 127 | 128 | static inline function get_videoFormats():Array { 129 | return kha.Assets.videoFormats; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /kala/asset/Builder.hx: -------------------------------------------------------------------------------- 1 | package kala.asset; 2 | 3 | import haxe.Json; 4 | import haxe.macro.Context; 5 | import haxe.macro.Expr.Field; 6 | import sys.io.File; 7 | 8 | class Builder { 9 | 10 | public static var resPath(default, null):String; 11 | public static var resJson(default, null):Dynamic; 12 | public static var files(default, null):Array; 13 | 14 | public static inline function findResources():String { 15 | return kha.internal.AssetsBuilder.findResources(); 16 | } 17 | 18 | macro public static function build(type:String):Array { 19 | var fields = Context.getBuildFields(); 20 | 21 | if (resPath == null) { 22 | resPath = findResources(); 23 | resJson = Json.parse(File.getContent(resPath + "files.json")); 24 | files = resJson.files; 25 | } 26 | 27 | var path = resPath.substr(0, resPath.length - 11) + '/'; 28 | 29 | switch(type) { 30 | case "FileArray": 31 | fields.push({ 32 | name: "files", 33 | doc: "The info of the files listed in files.json.", 34 | meta: [], 35 | access: [APublic, AStatic], 36 | kind: FProp("default", "never", macro :Array, macro $v{files}), 37 | pos: Context.currentPos() 38 | }); 39 | 40 | case "Sheet": 41 | var json:Dynamic; 42 | 43 | for (file in files) { 44 | var name:String = file.name; 45 | 46 | var ext = "_ssd"; 47 | 48 | if (file.type != "blob" || name.lastIndexOf(ext, name.length - ext.length) == -1) continue; 49 | 50 | name = name.substr(0, name.length - ext.length); 51 | json = Json.parse(File.getContent(path + file.files[0])); 52 | 53 | fields.push({ 54 | name: name, 55 | doc: null, 56 | meta: [], 57 | access: [APublic], 58 | kind: FProp("default", "never", macro :SheetData, macro new kala.asset.sheet.TexturePacker($v{json.frames})), 59 | pos: Context.currentPos() 60 | }); 61 | } 62 | } 63 | 64 | return fields; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /kala/asset/Loader.hx: -------------------------------------------------------------------------------- 1 | package kala.asset; 2 | 3 | import kala.EventHandle.CallbackHandle; 4 | import kha.FastFloat; 5 | 6 | @:access(kala.CallbackHandle) 7 | class Loader extends EventHandle { 8 | 9 | public var queuingAssets(get, never):Array; 10 | var _queuingAssets:Array = new Array(); 11 | 12 | public var loadedAssets(get, never):Array; 13 | var _loadedAssets:Array = new Array(); 14 | 15 | public var loading(get, never):Bool; 16 | public var paused(get, null):Bool; 17 | 18 | /** 19 | * The loading process in percent, between 0 and 1. 20 | */ 21 | public var percent(default, null):FastFloat = 0; 22 | public var totalSize(default, null):FastFloat = 0; 23 | public var loadedSize(default, null):FastFloat = 0; 24 | 25 | public var onProcess(default, null):CallbackHandleAssetLoadingInfo->AssetLoadingInfo->Bool>; 26 | 27 | /* 28 | * 0 - no action 29 | * 1 - loading 30 | * 2 - loading & waiting to stop 31 | * 3 - loading & waiting to pause 32 | * 4 - paused 33 | */ 34 | private var _state:Int = 0; 35 | 36 | public function new() { 37 | super(); 38 | onProcess = addCBHandle(new CallbackHandleAssetLoadingInfo->AssetLoadingInfo->Bool>()); 39 | } 40 | 41 | /** 42 | * Queue an asset for loading. 43 | * 44 | * @param type Type of the asset. 45 | * @param nameOrPath Name or path of the asset to be loaded. Taken as path if start with an '>' character or if asPath is set to true. 46 | * @param size Size of the asset, used to calculate the percent of loading process. 47 | * @param asPath If set to true, nameOrPath is taken as path otherwise as name. 48 | * @param readable If the asset is an image and is loaded from path, this will determine if the image is readable or not. 49 | * 50 | * @return This loader. 51 | */ 52 | public function queue(type:AssetType, nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false, readable:Bool = true):Loader { 53 | if (_state != 0) throw "Can't queue new asset while loading or paused."; 54 | 55 | totalSize += size < 1 ? 1 : size; 56 | 57 | var asset:AssetLoadingInfo; 58 | 59 | if (asPath || nameOrPath.charAt(0) == '>') { 60 | asset = new AssetLoadingInfo(type, null, nameOrPath, size, readable); 61 | } else { 62 | asset = new AssetLoadingInfo(type, nameOrPath, null, size); 63 | } 64 | 65 | _queuingAssets.push(asset); 66 | 67 | return this; 68 | } 69 | 70 | public inline function queueImage(nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false, readable:Bool = true):Loader { 71 | return queue(IMAGE, nameOrPath, size, asPath, readable); 72 | } 73 | 74 | public inline function queueSound(nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false):Loader { 75 | return queue(SOUND, nameOrPath, size, asPath); 76 | } 77 | 78 | public inline function queueFont(nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false):Loader { 79 | return queue(FONT, nameOrPath, size, asPath); 80 | } 81 | 82 | public inline function queueVideo(nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false):Loader { 83 | return queue(VIDEO, nameOrPath, size, asPath); 84 | } 85 | 86 | public inline function queueBlob(nameOrPath:String, ?size:FastFloat = 1, asPath:Bool = false):Loader { 87 | return queue(BLOB, nameOrPath, size, asPath); 88 | } 89 | 90 | /** 91 | * Queue all assets that were processed by Khamake except for shaders and assets which are already in the queuing list. 92 | * 93 | * @return This loader. 94 | */ 95 | public function queueAll(?sizes:Array):Loader { 96 | var i = 0; 97 | var type:AssetType; 98 | for (file in Assets.files) { 99 | if (file.type != "shader") { 100 | type = AssetType.createByName(cast(file.type, String).toUpperCase()); 101 | if (findQueuingAssetByName(type, file.name) == -1) { 102 | queue(type, file.name, sizes == null ? 1 : sizes[i]); 103 | } 104 | } 105 | 106 | i++; 107 | } 108 | 109 | return this; 110 | } 111 | 112 | public inline function bunble(assets:Array):Loader { 113 | for (asset in assets) { 114 | _queuingAssets.push(asset); 115 | totalSize += asset.size; 116 | } 117 | 118 | return this; 119 | } 120 | 121 | public function load(?onProcessCallback:Dynamic->AssetLoadingInfo->AssetLoadingInfo->Bool):AssetLoadingInfo { 122 | if (_queuingAssets.length > 0 && (_state == 0 || _state == 4)) { 123 | if (onProcessCallback != null) onProcess.notify(onProcessCallback); 124 | 125 | process(null); 126 | _state = 1; 127 | 128 | return _queuingAssets[0]; 129 | } 130 | 131 | return null; 132 | } 133 | 134 | public function reload():AssetLoadingInfo { 135 | if ((_queuingAssets.length > 0 || _loadedAssets.length > 0) && (_state == 0 || _state == 4)) { 136 | loadedSize = percent = 0; 137 | 138 | while (_loadedAssets.length > 0) { 139 | _queuingAssets.push(_loadedAssets.splice(0, 1)[0]); 140 | } 141 | 142 | process(null); 143 | _state = 1; 144 | 145 | return _queuingAssets[0]; 146 | } 147 | 148 | return null; 149 | } 150 | 151 | public inline function cancel():Loader { 152 | _state = 2; 153 | return this; 154 | } 155 | 156 | public inline function pause():Void { 157 | _state = 3; 158 | } 159 | 160 | function process(data:Dynamic):Void { 161 | var loadedAsset:AssetLoadingInfo = null; 162 | 163 | if (data != null) { 164 | loadedAsset = _queuingAssets.splice(0, 1)[0]; 165 | _loadedAssets.push(loadedAsset); 166 | loadedSize += loadedAsset.size; 167 | percent = loadedSize / totalSize; 168 | } 169 | 170 | var nextAsset = _queuingAssets.length > 0 ? _queuingAssets[0] : null; 171 | 172 | var i = 0; 173 | var callback:Dynamic->AssetLoadingInfo->AssetLoadingInfo->Bool; 174 | while (i < onProcess.count) { 175 | callback = onProcess._callbacks[i].cbFunction; 176 | if (callback(data, loadedAsset, nextAsset)) { 177 | onProcess._callbacks.splice(i, 1); 178 | } else i++; 179 | } 180 | 181 | if (nextAsset == null) { // all assets loaded 182 | _state = 0; 183 | } else { 184 | if (_state == 2) { // cancelled 185 | _queuingAssets.splice(0, _queuingAssets.length); 186 | _loadedAssets.splice(0, _loadedAssets.length); 187 | nextAsset = null; 188 | _state = 0; 189 | loadedSize = totalSize = percent = 0; 190 | } else if (_state == 3) { // paused 191 | nextAsset = null; 192 | _state = 4; 193 | } else { 194 | nextAsset.load(process); 195 | } 196 | } 197 | } 198 | 199 | function findQueuingAssetByName(type:AssetType, name:String):Int { 200 | for (i in 0..._queuingAssets.length) { 201 | if (_queuingAssets[i].type.getIndex() == type.getIndex() && _queuingAssets[i].name == name) { 202 | return i; 203 | } 204 | } 205 | 206 | return -1; 207 | } 208 | 209 | inline function get_loading():Bool { 210 | return _state > 0 && _state != 4; 211 | } 212 | 213 | inline function get_paused():Bool { 214 | return _state == 4; 215 | } 216 | 217 | inline function get_queuingAssets():Array { 218 | return _queuingAssets.copy(); 219 | } 220 | 221 | inline function get_loadedAssets():Array { 222 | return _loadedAssets.copy(); 223 | } 224 | 225 | } 226 | 227 | class AssetLoadingInfo { 228 | 229 | public var name:String; 230 | public var path:String; 231 | public var size:FastFloat; 232 | public var type:AssetType; 233 | public var readable:Bool; 234 | 235 | public inline function new(type:AssetType, name:String, ?path:String, ?size:FastFloat = 1, readable:Bool = true) { 236 | this.type = type; 237 | this.name = name; 238 | this.path = path; 239 | this.size = size; 240 | this.readable = readable; 241 | } 242 | 243 | @:extern 244 | public inline function load(callback:Dynamic->Void):Void { 245 | if (name == null) { 246 | if (path == null) return; 247 | 248 | switch(type) { 249 | case IMAGE: Assets.loadImageFromPath(path, readable, callback); 250 | case SOUND: Assets.loadSoundFromPath(path, callback); 251 | case FONT: Assets.loadFontFromPath(path, callback); 252 | case VIDEO: Assets.loadVideoFromPath(path, callback); 253 | case BLOB: Assets.loadBlobFromPath(path, callback); 254 | } 255 | } else { 256 | switch(type) { 257 | case IMAGE: Assets.loadImage(name, callback); 258 | case SOUND: Assets.loadSound(name, callback); 259 | case FONT: Assets.loadFont(name, callback); 260 | case VIDEO: Assets.loadVideo(name, callback); 261 | case BLOB: Assets.loadBlob(name, callback); 262 | } 263 | } 264 | } 265 | 266 | } -------------------------------------------------------------------------------- /kala/asset/sheet/SheetData.hx: -------------------------------------------------------------------------------- 1 | package kala.asset.sheet; 2 | 3 | import haxe.ds.StringMap; 4 | import kala.objects.sprite.Sprite.SpriteData; 5 | import kha.Image; 6 | 7 | class SheetData { 8 | 9 | private var _frames:StringMap = new StringMap(); 10 | 11 | public function new(framesData:Array) { 12 | 13 | } 14 | 15 | public function get(key:String, ?image:Image):SpriteData { 16 | return null; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /kala/asset/sheet/TexturePacker.hx: -------------------------------------------------------------------------------- 1 | package kala.asset.sheet; 2 | 3 | import kala.math.Rect.RectI; 4 | import kala.objects.sprite.Sprite.SpriteData; 5 | import kha.Image; 6 | 7 | class TexturePacker extends SheetData { 8 | 9 | public function new(framesData:Array) { 10 | super(framesData); 11 | 12 | var frame:SpriteData; 13 | for (data in framesData) { 14 | frame = new SpriteData(data.filename, null, [new RectI(data.frame.x, data.frame.y, data.frame.w, data.frame.h)], 0); 15 | _frames.set(data.filename, frame); 16 | } 17 | } 18 | 19 | override public function get(key:String, ?image:Image):SpriteData { 20 | var spriteData:SpriteData; 21 | 22 | if (key.charAt(key.length - 1) == '/' || key.charAt(key.length - 1) == '\\') { 23 | spriteData = new SpriteData(key, image, new Array(), -1); 24 | 25 | var frameKeys = [for (key in _frames.keys()) key]; 26 | frameKeys.sort(function(a, b) { 27 | a = a.toLowerCase(); 28 | b = b.toLowerCase(); 29 | 30 | if (a < b) return -1; 31 | if (a > b) return 1; 32 | 33 | return 0; 34 | }); 35 | 36 | for (frameKey in frameKeys) { 37 | if (frameKey.indexOf(key) == 0) { 38 | spriteData.frames.push(_frames.get(frameKey).frames[0]); 39 | } 40 | } 41 | 42 | return spriteData; 43 | } 44 | 45 | spriteData = _frames.get(key); 46 | 47 | if (spriteData == null) throw 'key "$key" not found. If the sprite data is an animation, remember to add "/" at the end of key.'; 48 | 49 | spriteData = spriteData.clone(); 50 | spriteData.image = image; 51 | 52 | return spriteData; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /kala/audio/Audio.hx: -------------------------------------------------------------------------------- 1 | package kala.audio; 2 | 3 | import kha.Sound; 4 | 5 | @:allow(kala.Kala) 6 | @:allow(kala.audio.AudioChannel) 7 | class Audio { 8 | 9 | public static var groups(default, null):Array = new Array(); 10 | 11 | public static function play(sound:Sound, volume:Float = 1, loop:Bool = false):AudioChannel { 12 | var channel = new AudioChannel(kha.audio2.Audio1.play(sound, loop), null, false); 13 | channel.volume = volume; 14 | return channel; 15 | } 16 | 17 | public static function stream(sound:Sound, volume:Float = 1, loop:Bool = false):AudioChannel { 18 | var channel = new AudioChannel(kha.audio2.Audio1.stream(sound, loop), null, false); 19 | channel.volume = volume; 20 | return channel; 21 | } 22 | 23 | static function update():Void { 24 | for (group in groups) { 25 | var channels = group.channels; 26 | var i = channels.length; 27 | while (i-- > 0) { 28 | if (channels[i].finished && !channels[i].kept) { 29 | channels.splice(i, 1); 30 | } 31 | } 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /kala/audio/AudioChannel.hx: -------------------------------------------------------------------------------- 1 | package kala.audio; 2 | 3 | class AudioChannel { 4 | 5 | public var channel:kha.audio1.AudioChannel; 6 | 7 | public var group:AudioGroup; 8 | public var kept:Bool; 9 | 10 | public var finished(get, never):Bool; 11 | public var length(get, never):Float; 12 | public var position(get, never):Float; 13 | 14 | public var volume(get, set):Float; 15 | var _volume:Float; 16 | 17 | public var muted(default, set):Bool; 18 | 19 | public inline function new(channel:kha.audio1.AudioChannel, group:AudioGroup, kept:Bool) { 20 | this.channel = channel; 21 | this.group = group; 22 | this.kept = kept; 23 | muted = false; 24 | } 25 | 26 | @:extern 27 | public inline function play():Void { 28 | channel.play(); 29 | } 30 | 31 | @:extern 32 | public inline function pause():Void { 33 | channel.pause(); 34 | } 35 | 36 | @:extern 37 | public inline function stop():Void { 38 | channel.stop(); 39 | if (!kept && group != null) group.channels.remove(this); 40 | } 41 | 42 | function updateVolume():Void { 43 | if (muted) { 44 | channel.volume = 0; 45 | return; 46 | } 47 | 48 | if (group != null) { 49 | if (group.muted) channel.volume = 0; 50 | else channel.volume = _volume * group.volume; 51 | return; 52 | } 53 | 54 | channel.volume = _volume; 55 | } 56 | 57 | inline function get_finished():Bool { 58 | return channel.finished; 59 | } 60 | 61 | inline function get_length():Float { 62 | return channel.length; 63 | } 64 | 65 | inline function get_position():Float { 66 | return channel.position; 67 | } 68 | 69 | inline function get_volume():Float { 70 | return _volume; 71 | } 72 | 73 | inline function set_volume(value:Float):Float { 74 | _volume = value; 75 | updateVolume(); 76 | return value; 77 | } 78 | 79 | inline function set_muted(value:Bool):Bool { 80 | muted = value; 81 | updateVolume(); 82 | return value; 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /kala/audio/AudioGroup.hx: -------------------------------------------------------------------------------- 1 | package kala.audio; 2 | 3 | import kha.Sound; 4 | 5 | @:access(kala.audio.AudioChannel) 6 | @:allow(kala.audio.AudioChannel) 7 | class AudioGroup { 8 | 9 | public var channels(default, null):Array; 10 | 11 | public var volume(get, set):Float; 12 | var _volume:Float; 13 | 14 | public var muted(default, set):Bool; 15 | 16 | public function new() { 17 | channels = new Array(); 18 | _volume = 1; 19 | } 20 | 21 | public inline function play(sound:Sound, volume:Float = 1, loop:Bool = false, kept:Bool = false):AudioChannel { 22 | var khaChannel = kha.audio2.Audio1.play(sound, loop); 23 | if (khaChannel == null) return null; 24 | 25 | var channel = new AudioChannel(khaChannel, this, kept); 26 | channel._volume = volume; 27 | channel.channel.volume = muted ? 0 : volume * _volume; 28 | channels.push(channel); 29 | return channel; 30 | } 31 | 32 | public inline function stream(group:String, sound:Sound, ?volume:Float = 1, loop:Bool = false, kept:Bool = false):AudioChannel { 33 | var khaChannel = kha.audio2.Audio1.stream(sound, loop); 34 | if (khaChannel == null) return null; 35 | 36 | var channel = new AudioChannel(khaChannel, this, kept); 37 | channel._volume = volume; 38 | channel.channel.volume = muted ? 0 : volume * _volume; 39 | channels.push(channel); 40 | return channel; 41 | } 42 | 43 | inline function update():Void { 44 | var i = channels.length; 45 | while (i-- > 0) { 46 | if (channels[i].finished && !channels[i].kept) { 47 | channels.splice(i, 1); 48 | } 49 | } 50 | } 51 | 52 | function get_volume():Float { 53 | return _volume; 54 | } 55 | 56 | function set_volume(value:Float):Float { 57 | _volume = value; 58 | for (channel in channels) { 59 | channel.updateVolume(); 60 | } 61 | return value; 62 | } 63 | 64 | function set_muted(value:Bool):Bool { 65 | muted = value; 66 | for (channel in channels) { 67 | channel.updateVolume(); 68 | } 69 | return value; 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /kala/behaviors/Behavior.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors; 2 | 3 | import kala.EventHandle; 4 | import kala.objects.Object; 5 | 6 | interface IBehavior { 7 | 8 | public function destroy():Void; 9 | public function reset():Void; 10 | public function deepReset():Void; 11 | public function remove():Void; 12 | 13 | } 14 | 15 | class Behavior extends EventHandle implements IBehavior { 16 | 17 | public var object(default, null):T; 18 | 19 | // 20 | 21 | public var onDestroy(default, null):CallbackHandle->Void>; 22 | public var onReset(default, null):CallbackHandle->Void>; 23 | 24 | public function new(?object:T) { 25 | super(); 26 | 27 | onDestroy = addCBHandle(new CallbackHandle->Void>()); 28 | onReset = addCBHandle(new CallbackHandle->Void>()); 29 | reset(); 30 | 31 | if (object != null) addTo(object); 32 | } 33 | 34 | public function reset():Void { 35 | for (callback in onReset) callback.cbFunction(this); 36 | } 37 | 38 | public function destroy():Void { 39 | remove(); 40 | 41 | onDestroy = null; 42 | onReset = null; 43 | } 44 | 45 | public function deepReset():Void { 46 | reset(); 47 | remove(); 48 | clearCBHandles(); 49 | } 50 | 51 | public function addTo(object:T):Behavior { 52 | if (this.object != null) remove(); 53 | 54 | this.object = object; 55 | object._behaviors.push(this); 56 | 57 | return this; 58 | } 59 | 60 | public function remove():Void { 61 | if (object != null) { 62 | object._behaviors.remove(this); 63 | object = null; 64 | } 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /kala/behaviors/Timer.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors; 2 | import kala.behaviors.tween.Ease; 3 | 4 | import kala.behaviors.tween.Ease.EaseFunction; 5 | import kala.behaviors.tween.Tween; 6 | import kala.behaviors.Behavior; 7 | import kala.objects.Object; 8 | import kala.util.types.Pair; 9 | import kha.FastFloat; 10 | 11 | class Timer extends Behavior { 12 | 13 | private var _coolingDownIDs:Array> = new Array>(); 14 | private var _coolingDownFunctions:ArrayVoid, FastFloat>> = new ArrayVoid, FastFloat>>(); 15 | 16 | private var _loopTasks:Array = new Array(); 17 | 18 | override public function reset():Void { 19 | super.reset(); 20 | 21 | _coolingDownIDs.splice(0, _coolingDownIDs.length); 22 | _coolingDownFunctions.splice(0, _coolingDownFunctions.length); 23 | _loopTasks.splice(0, _loopTasks.length); 24 | } 25 | 26 | override public function destroy():Void { 27 | super.destroy(); 28 | 29 | _coolingDownIDs = null; 30 | _coolingDownFunctions = null; 31 | 32 | while (_loopTasks.length > 0) _loopTasks.pop().destroy(); 33 | _loopTasks = null; 34 | } 35 | 36 | override public function addTo(object:Object):Timer { 37 | super.addTo(object); 38 | object.onPostUpdate.notifyPrivateCB(this, update); 39 | return this; 40 | } 41 | 42 | override public function remove():Void { 43 | if (object != null) { 44 | object.onPostUpdate.removePrivateCB(this, update); 45 | } 46 | 47 | super.remove(); 48 | } 49 | 50 | public function cooldown(?id:Int, coolingTime:FastFloat, func:Void->Void):Bool { 51 | if (id == null) { 52 | for (cdFunc in _coolingDownFunctions) { 53 | if (cdFunc.a == func) return false; 54 | } 55 | 56 | _coolingDownFunctions.push(new PairVoid, Int>(func, coolingTime)); 57 | func(); 58 | 59 | return true; 60 | } 61 | 62 | for (cdID in _coolingDownIDs) { 63 | if (cdID.a == id) return false; 64 | } 65 | 66 | _coolingDownIDs.push(new Pair(id, coolingTime)); 67 | func(); 68 | 69 | return true; 70 | } 71 | 72 | public function delay(delayTime:FastFloat, func:LoopTask->Void):LoopTask { 73 | var task = new LoopTask(this, delayTime, 1, func, null); 74 | _loopTasks.push(task); 75 | return task; 76 | } 77 | 78 | public function loop(duration:FastFloat, execTimes:Int, ?execFirst:Bool = false, onExecute:LoopTask->Void, ?onComplete:LoopTask->Void):LoopTask { 79 | var task = new LoopTask(this, duration, execTimes, onExecute, onComplete); 80 | _loopTasks.push(task); 81 | 82 | if (execFirst) { 83 | task.elapsedExecutions++; 84 | onExecute(task); 85 | } 86 | 87 | return task; 88 | } 89 | 90 | function update(obj:Object, elapsed:FastFloat):Void { 91 | for (cdID in _coolingDownIDs.copy()) { 92 | cdID.b -= elapsed ; 93 | if (cdID.b <= 0) _coolingDownIDs.remove(cdID); 94 | } 95 | 96 | for (cdFunc in _coolingDownFunctions.copy()) { 97 | cdFunc.b -= elapsed; 98 | if (cdFunc.b <= 0) _coolingDownFunctions.remove(cdFunc); 99 | } 100 | 101 | for (task in _loopTasks.copy()) { 102 | task.elapsedTime += elapsed; 103 | 104 | if (task.elapsedTime >= task.duration) { 105 | task.elapsedTime = 0; 106 | 107 | task.elapsedExecutions++; 108 | task.onExecCB(task); 109 | 110 | if (task.totalExecutions > 0 && (task.elapsedExecutions == task.totalExecutions)) { 111 | if (task.onCompleteCB != null) task.onCompleteCB(task); 112 | task.cancel(); 113 | } 114 | } 115 | } 116 | } 117 | 118 | } 119 | 120 | @:allow(kala.behaviors.Timer) 121 | @:access(kala.behaviors.Timer) 122 | class LoopTask { 123 | 124 | public var duration:FastFloat; 125 | public var elapsedTime(default, null):FastFloat; 126 | 127 | public var totalExecutions:UInt; 128 | public var elapsedExecutions(default, null):UInt; 129 | 130 | public var onExecCB(default, null):LoopTask->Void; 131 | public var onCompleteCB(default, null):LoopTask->Void; 132 | 133 | private var _manager:Timer; 134 | 135 | private inline function new(manager:Timer, duration:Int, totalExecutions:UInt, onExecCB:LoopTask->Void, onCompleteCB:LoopTask->Void) { 136 | this._manager = manager; 137 | 138 | this.duration = duration; 139 | this.totalExecutions = totalExecutions; 140 | this.onExecCB = onExecCB; 141 | this.onCompleteCB = onCompleteCB; 142 | 143 | elapsedTime = 0; 144 | elapsedExecutions = 0; 145 | } 146 | 147 | @:extern 148 | public inline function cancel():Void { 149 | _manager._loopTasks.remove(this); 150 | _manager = null; 151 | } 152 | 153 | @:extern 154 | inline function destroy():Void { 155 | onExecCB = null; 156 | onCompleteCB = null; 157 | _manager = null; 158 | } 159 | 160 | } 161 | 162 | // 163 | 164 | @:access(kala.behaviors.tween.Tween) 165 | class TimerEx extends Timer { 166 | 167 | public var _tween:Tween; 168 | 169 | public function new() { 170 | super(); 171 | 172 | _tween = new Tween(); 173 | } 174 | 175 | override public function destroy():Void { 176 | super.destroy(); 177 | _tween = null; 178 | } 179 | 180 | override public function addTo(object:Object):TimerEx { 181 | super.addTo(object); 182 | _tween.object = object; 183 | return this; 184 | } 185 | 186 | override public function remove():Void { 187 | super.remove(); 188 | _tween.object = null; 189 | } 190 | 191 | override function update(obj:Object, elapsed:FastFloat):Void { 192 | super.update(obj, elapsed); 193 | _tween.update(obj, elapsed); 194 | } 195 | 196 | public function timeline( 197 | ?target:Dynamic, ?ease:EaseFunction, ?onTweenUpdateCB:TweenTask->Void 198 | ):TweenTimeline { 199 | return _tween.get(target, ease, onTweenUpdateCB); 200 | } 201 | 202 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/BaseCollider.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision; 2 | 3 | import kala.behaviors.Behavior; 4 | import kala.math.color.Color; 5 | import kala.objects.Object; 6 | import kha.Canvas; 7 | import kha.FastFloat; 8 | 9 | #if (debug || kala_debug) 10 | import kala.debug.Debug; 11 | #end 12 | 13 | @:access(kala.objects.Object) 14 | @:allow(kala.behaviors.collision.transformable.shapes.BaseCollisionShape) 15 | class BaseCollider extends Behavior { 16 | 17 | #if (debug || kala_debug) 18 | private static var _debugDrawCalls:Array = Debug.addDrawLayer(); 19 | #end 20 | 21 | public var debugColor:Color = 0xffff0000; 22 | public var debugFill:Bool = false; 23 | public var debugLineStrenght:UInt = 2; 24 | 25 | public var shapes(default, null):Array = new Array(); 26 | 27 | public var available(default, null):Bool = false; 28 | 29 | override public function reset():Void { 30 | super.reset(); 31 | while (shapes.length > 0) shapes.pop().put(); 32 | available = false; 33 | } 34 | 35 | override public function destroy():Void { 36 | super.destroy(); 37 | while (shapes.length > 0) shapes.pop().put(); 38 | shapes = null; 39 | } 40 | 41 | override public function addTo(object:T):BaseCollider { 42 | super.addTo(object); 43 | object.onPostDraw.notifyPrivateCB(this, postDrawUpdate); 44 | return this; 45 | } 46 | 47 | override public function remove():Void { 48 | available = false; 49 | object.onPostDraw.removePrivateCB(this, postDrawUpdate); 50 | super.remove(); 51 | } 52 | 53 | public function testPoint(pointX:FastFloat, pointY:FastFloat):Bool { 54 | if (!available) return false; 55 | 56 | for (shape in shapes) { 57 | if (shape.active && shape.testPoint(pointX, pointY)) return true; 58 | } 59 | 60 | return false; 61 | } 62 | 63 | public function drawDebug(color:UInt, ?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Bool { 64 | if (object == null) return false; 65 | 66 | canvas.g2.color = color; 67 | canvas.g2.opacity = 1; 68 | 69 | return true; 70 | } 71 | 72 | function postDrawUpdate(obj:Object, data:DrawingData, canvas:Canvas):Void { 73 | available = true; 74 | 75 | for (shape in shapes) { 76 | if (!shape.active) continue; 77 | shape.update(obj._cachedDrawingMatrix); 78 | } 79 | 80 | #if (debug || kala_debug) 81 | if (Debug.collisionDebug) { 82 | _debugDrawCalls.push( 83 | new DebugDrawCall( 84 | canvas, 85 | function(canvas) { 86 | drawDebug(debugColor, debugFill, debugLineStrenght, canvas); 87 | } 88 | ) 89 | ); 90 | } 91 | #end 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/BaseCollisionShape.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision; 2 | 3 | import kala.behaviors.collision.transformable.Collider; 4 | import kala.math.Matrix; 5 | import kala.math.Position; 6 | import kha.FastFloat; 7 | 8 | @:allow(kala.behaviors.collision.BaseCollider) 9 | class BaseCollisionShape { 10 | 11 | public var position:Position = new Position(); 12 | public var available(get, never):Bool; 13 | public var active:Bool; 14 | 15 | public function new() { 16 | 17 | } 18 | 19 | public function reset():Void { 20 | position.setOrigin(); 21 | active = true; 22 | } 23 | 24 | public function destroy():Void { 25 | position = null; 26 | } 27 | 28 | public function put():Void { 29 | 30 | } 31 | 32 | public function testPoint(pointX:FastFloat, pointY:FastFloat):Bool { 33 | return false; 34 | } 35 | 36 | function update(objectMatrix:Matrix):Void { 37 | 38 | } 39 | 40 | function get_available():Bool { 41 | return false; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/basic/Collider.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.basic; 2 | 3 | import kala.behaviors.collision.BaseCollider; 4 | import kala.behaviors.collision.basic.shapes.CollisionCircle; 5 | import kala.behaviors.collision.basic.shapes.CollisionRectangle; 6 | import kala.behaviors.collision.basic.shapes.CollisionShape; 7 | import kala.behaviors.collision.basic.shapes.ShapeType; 8 | import kala.math.Matrix; 9 | import kala.objects.Object; 10 | import kha.Canvas; 11 | import kha.FastFloat; 12 | 13 | /** 14 | * Used for lightweight collision detection between shapes. 15 | * Does not support transformation & collision result data. 16 | */ 17 | class Collider extends BaseCollider { 18 | 19 | private var _shapes:Array; 20 | 21 | public function new(?object:Object) { 22 | super(object); 23 | _shapes = cast shapes; 24 | } 25 | 26 | override public function destroy():Void { 27 | super.destroy(); 28 | _shapes = null; 29 | } 30 | 31 | override public function drawDebug(color:UInt, ?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Bool { 32 | if (!super.drawDebug(color, fill, lineStrenght, canvas)) return false; 33 | for (shape in _shapes) if (shape.active) shape.drawDebug(fill, lineStrenght, canvas); 34 | return true; 35 | } 36 | 37 | public inline function removeShape(shape:CollisionShape):Bool { 38 | return _shapes.remove(shape); 39 | } 40 | 41 | public inline function addShape(shape:CollisionShape):CollisionShape { 42 | _shapes.push(shape); 43 | return shape; 44 | } 45 | 46 | public inline function addCircle(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 47 | var circle = CollisionCircle.get(); 48 | circle.position.setXY(x, y); 49 | circle.radius = radius; 50 | _shapes.push(circle); 51 | return circle; 52 | } 53 | 54 | public inline function addRect(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionRectangle { 55 | var rect = CollisionRectangle.get(); 56 | rect.position.setXY(x, y); 57 | rect.setSize(width, height); 58 | _shapes.push(rect); 59 | return rect; 60 | } 61 | 62 | public inline function addObjectRect(scaleX:FastFloat = 1, scaleY:FastFloat = 1):CollisionRectangle { 63 | var rect = addRect(0, 0, object.width * scaleX, object.height * scaleY); 64 | rect.position.setXY((object.width - rect.width) / 2, (object.height - rect.height) / 2); 65 | return rect; 66 | } 67 | 68 | public function test(collider:Collider):Bool { 69 | if (!available) return false; 70 | 71 | for (shapeA in _shapes) { 72 | if (!shapeA.active) continue; 73 | for (shapeB in collider._shapes) { 74 | if (shapeB.active && shapeA.test(shapeB)) return true; 75 | } 76 | } 77 | 78 | return false; 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/basic/shapes/CollisionCircle.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.basic.shapes; 2 | 3 | import kala.behaviors.collision.basic.shapes.CollisionCircle; 4 | import kala.behaviors.collision.basic.shapes.CollisionRectangle; 5 | import kala.behaviors.collision.basic.shapes.ShapeType; 6 | import kala.math.Collision; 7 | import kala.util.pool.Pool; 8 | import kha.Canvas; 9 | import kha.FastFloat; 10 | 11 | using kha.graphics2.GraphicsExtension; 12 | 13 | class CollisionCircle extends CollisionShape { 14 | 15 | public static var pool(default, never) = new Pool(function() return new CollisionCircle()); 16 | 17 | public static inline function get():CollisionCircle { 18 | var circle = pool.get(); 19 | circle.reset(); 20 | return circle; 21 | } 22 | 23 | // 24 | 25 | public var radius:FastFloat; 26 | 27 | public function new() { 28 | super(); 29 | type = ShapeType.CIRCLE; 30 | } 31 | 32 | override public function testCircle(circle:CollisionCircle):Bool { 33 | return Collision.fastCircleVsCircle( 34 | absX, absY, radius, 35 | circle.absX, circle.absY, circle.radius 36 | ); 37 | } 38 | 39 | override public function testRect(rect:CollisionRectangle):Bool { 40 | return Collision.fastCircleVsRect( 41 | absX, absY, radius, 42 | rect.absX, rect.absY, rect.width, rect.height 43 | ); 44 | } 45 | 46 | override public function drawDebug(?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Void { 47 | super.drawDebug(fill, lineStrenght, canvas); 48 | if (fill) canvas.g2.fillCircle(0, 0, radius); 49 | else canvas.g2.drawCircle(0, 0, radius, lineStrenght); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/basic/shapes/CollisionRectangle.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.basic.shapes; 2 | 3 | import kala.behaviors.collision.basic.shapes.ShapeType; 4 | import kala.math.Collision; 5 | import kala.util.pool.Pool; 6 | import kha.Canvas; 7 | import kha.FastFloat; 8 | 9 | class CollisionRectangle extends CollisionShape { 10 | 11 | public static var pool(default, never) = new Pool(function() return new CollisionRectangle()); 12 | 13 | public static inline function get():CollisionRectangle { 14 | var rect = pool.get(); 15 | rect.reset(); 16 | return rect; 17 | } 18 | 19 | // 20 | 21 | public var width:FastFloat; 22 | public var height:FastFloat; 23 | 24 | public function new() { 25 | super(); 26 | type = ShapeType.RECTANGLE; 27 | } 28 | 29 | override public function testPoint(pointX:FastFloat, pointY:FastFloat):Bool { 30 | return Collision.pointVsRect(pointX, pointY, absX, absY, width, height); 31 | } 32 | 33 | override public function testCircle(circle:CollisionCircle):Bool { 34 | return Collision.fastCircleVsRect( 35 | circle.absX, circle.absY, circle.radius, 36 | absX, absY, width, height 37 | ); 38 | } 39 | 40 | override public function testRect(rect:CollisionRectangle):Bool { 41 | return Collision.fastRectVsRect( 42 | absX, absY, width, height, 43 | rect.absX, rect.absY, rect.width, rect.height 44 | ); 45 | } 46 | 47 | public inline function setSize(width:FastFloat, height:FastFloat):Void { 48 | this.width = width; 49 | this.height = height; 50 | } 51 | 52 | override public function drawDebug(?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Void { 53 | super.drawDebug(fill, lineStrenght, canvas); 54 | if (fill) canvas.g2.fillRect(0, 0, width, height); 55 | else canvas.g2.drawRect(0, 0, width, height, lineStrenght); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/basic/shapes/CollisionShape.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.basic.shapes; 2 | 3 | import kala.behaviors.collision.BaseCollisionShape; 4 | import kala.behaviors.collision.basic.shapes.ShapeType; 5 | import kala.math.Matrix; 6 | import kha.Canvas; 7 | import kha.FastFloat; 8 | 9 | @:access(kala.objects.Object) 10 | class CollisionShape extends BaseCollisionShape { 11 | 12 | public var type(default, null):ShapeType; 13 | public var dynamicPosition:Bool; 14 | public var absX(default, null):Null; 15 | public var absY(default, null):Null; 16 | 17 | override public function reset():Void { 18 | super.reset(); 19 | dynamicPosition = true; 20 | } 21 | 22 | public inline function test(shape:CollisionShape):Bool { 23 | return switch(shape.type) { 24 | case ShapeType.CIRCLE: testCircle(cast shape); 25 | case ShapeType.RECTANGLE: testRect(cast shape); 26 | } 27 | } 28 | 29 | public function testCircle(circle:CollisionCircle):Bool { 30 | return false; 31 | } 32 | 33 | public function testRect(rect:CollisionRectangle):Bool { 34 | return false; 35 | } 36 | 37 | public function drawDebug(?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Void { 38 | canvas.g2.transformation = Matrix.translation(absX, absY); 39 | } 40 | 41 | override function update(objectMatrix:Matrix):Void { 42 | var matrix:Matrix = ( 43 | dynamicPosition ? 44 | objectMatrix.multmat(Matrix.translation(position.realX, position.realY)) : 45 | Matrix.translation(position.realX, position.realY).multmat(objectMatrix) 46 | ); 47 | absX = matrix.tx; 48 | absY = matrix.ty; 49 | } 50 | 51 | override function get_available():Bool { 52 | return absX != null && absY != null; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/basic/shapes/ShapeType.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.basic.shapes; 2 | 3 | @:enum 4 | abstract ShapeType(Int) { 5 | 6 | var CIRCLE = 0; 7 | var RECTANGLE = 1; 8 | 9 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/transformable/Collider.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.transformable; 2 | 3 | import kala.behaviors.collision.transformable.shapes.CollisionCircle; 4 | import kala.behaviors.collision.transformable.shapes.CollisionPolygon; 5 | import kala.behaviors.collision.transformable.shapes.CollisionShape; 6 | import kala.math.Vec2; 7 | import kala.objects.Object; 8 | import kha.Canvas; 9 | import kha.FastFloat; 10 | 11 | using kha.graphics2.GraphicsExtension; 12 | 13 | /** 14 | * Used for SAT supported collision detection. 15 | */ 16 | class Collider extends BaseCollider { 17 | 18 | private var _shapes:Array; 19 | 20 | public function new(?object:Object) { 21 | super(object); 22 | _shapes = cast shapes; 23 | } 24 | 25 | override public function destroy():Void { 26 | super.destroy(); 27 | _shapes = null; 28 | } 29 | 30 | override public function drawDebug(color:UInt, ?fill:Bool = false, ?lineStrenght:FastFloat = 1, canvas:Canvas):Bool { 31 | if (!super.drawDebug(color, fill, lineStrenght, canvas)) return false; 32 | 33 | for (shape in _shapes) { 34 | if (!shape.active) continue; 35 | 36 | canvas.g2.transformation = shape.matrix; 37 | 38 | if (fill) { 39 | canvas.g2.fillPolygon( 40 | 0, 0, 41 | Vec2.toVector2Array(shape.getVertices()) 42 | ); 43 | } else { 44 | canvas.g2.drawPolygon( 45 | 0, 0, 46 | Vec2.toVector2Array(shape.getVertices()), 47 | lineStrenght 48 | ); 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | public function addCircle(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 56 | var circle = CollisionCircle.get(); 57 | circle.position.setXY(x, y); 58 | circle.radius = radius; 59 | _shapes.push(circle); 60 | 61 | return circle; 62 | } 63 | 64 | public function addRect(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionPolygon { 65 | var rect = CollisionPolygon.get(); 66 | rect.position.setXY(x, y); 67 | rect.vertices = [ 68 | new Vec2(0, 0), 69 | new Vec2(0, height), 70 | new Vec2(width, height), 71 | new Vec2(width, 0) 72 | ]; 73 | _shapes.push(rect); 74 | 75 | return rect; 76 | } 77 | 78 | // TODO: If concave set to true, break polygon into multiple triangles. 79 | public function addPolygon(x:FastFloat, y:FastFloat, vertices:Array, concave:Bool = false):Array { 80 | if (concave) return null; 81 | 82 | var polygon = CollisionPolygon.get(); 83 | polygon.position.setXY(x, y); 84 | polygon.vertices = vertices.copy(); 85 | _shapes.push(polygon); 86 | 87 | return [polygon]; 88 | } 89 | 90 | public inline function addObjectRect():CollisionPolygon { 91 | return addRect(0, 0, object.width, object.height); 92 | } 93 | 94 | public inline function addShape(shape:CollisionShape):CollisionShape { 95 | _shapes.push(shape); 96 | return shape; 97 | } 98 | 99 | public inline function removeShape(shape:CollisionShape):Bool { 100 | return _shapes.remove(shape); 101 | } 102 | 103 | public function test(collider:Collider):CollisionResult { 104 | if (!available) return null; 105 | 106 | var result:CollisionResult; 107 | 108 | for (shapeA in _shapes) { 109 | if (!shapeA.active) continue; 110 | for (shapeB in collider._shapes) { 111 | if (!shapeB.active) continue; 112 | result = shapeA.test(shapeB); 113 | if (result != null) return result; 114 | } 115 | } 116 | 117 | return null; 118 | } 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /kala/behaviors/collision/transformable/CollisionResult.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.transformable; 2 | 3 | import kala.behaviors.collision.transformable.shapes.CollisionShape; 4 | import kala.math.Collision.CollisionData; 5 | import kala.math.Vec2; 6 | import kha.FastFloat; 7 | 8 | 9 | class CollisionResult { 10 | 11 | public var shape1:CollisionShape; 12 | public var shape2:CollisionShape; 13 | public var data:CollisionData; 14 | 15 | public inline function new(shape1:CollisionShape, shape2:CollisionShape, data:CollisionData) { 16 | this.shape1 = shape1; 17 | this.shape2 = shape2; 18 | this.data = data; 19 | } 20 | 21 | @:extern 22 | public inline function flip():CollisionResult { 23 | var shape = shape1; 24 | shape1 = shape2; 25 | shape2 = shape; 26 | 27 | data.flip(); 28 | 29 | return this; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/transformable/shapes/CollisionCircle.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.transformable.shapes; 2 | 3 | import kala.behaviors.collision.transformable.CollisionResult; 4 | import kala.math.Collision; 5 | import kala.math.Vec2; 6 | import kala.util.pool.Pool; 7 | import kha.FastFloat; 8 | 9 | class CollisionCircle extends CollisionShape { 10 | 11 | public static var pool(default, never) = new Pool(function() return new CollisionCircle()); 12 | 13 | public static inline function get():CollisionCircle { 14 | var circle = pool.get(); 15 | circle.reset(); 16 | return circle; 17 | } 18 | 19 | // 20 | 21 | public var radius(default, set):FastFloat; 22 | public var segments(default, set):Int; 23 | 24 | private var _verticesUpdated:Bool; 25 | 26 | public function new() { 27 | super(); 28 | isCircle = true; 29 | } 30 | 31 | override public function reset():Void { 32 | super.reset(); 33 | segments = 0; 34 | } 35 | 36 | override public function put():Void { 37 | pool.putUnsafe(this); 38 | } 39 | 40 | override public function testCircle(circle:CollisionCircle):CollisionResult { 41 | var otherStillCircle = circle.hasSymmetricalTransformation(); 42 | 43 | var otherX = circle.matrix._20; 44 | var otherY = circle.matrix._21; 45 | var otherRadius = circle.radius * circle.matrix._00; 46 | 47 | var data:CollisionData; 48 | 49 | if (hasSymmetricalTransformation()) { 50 | var x = matrix._20; 51 | var y = matrix._21; 52 | var radius = radius * matrix._00; 53 | 54 | if (otherStillCircle) { 55 | data = Collision.circleVsCircle(x, y, radius, otherX, otherY, otherRadius); 56 | if (data == null) return null; 57 | return new CollisionResult(this, circle, data); 58 | } 59 | 60 | data = Collision.circleVsPolygon(x, y, radius, circle.getTransformedVertices()); 61 | if (data == null) return null; 62 | return new CollisionResult(this, circle, data); 63 | } 64 | 65 | if (otherStillCircle) { 66 | data = Collision.circleVsPolygon(otherX, otherY, otherRadius, getTransformedVertices()); 67 | if (data == null) return null; 68 | return new CollisionResult(circle, this, data.flip()); 69 | } 70 | 71 | var result = Collision.polygonVsPolygon(getTransformedVertices(), circle.getTransformedVertices()); 72 | 73 | if (result == null) return null; 74 | 75 | if (result.b) { 76 | return new CollisionResult(circle, this, result.a); 77 | } 78 | 79 | return new CollisionResult(this, circle, result.a); 80 | } 81 | 82 | override public function testPolygon(polygon:CollisionPolygon):CollisionResult { 83 | var polyVertices = polygon.getTransformedVertices(); 84 | 85 | var data:CollisionData; 86 | 87 | if (hasSymmetricalTransformation()) { 88 | data = Collision.circleVsPolygon(matrix._20, matrix._21, radius * matrix._00, polygon.getTransformedVertices()); 89 | if (data == null) return null; 90 | return new CollisionResult(this, polygon, data); 91 | } 92 | 93 | var result = Collision.polygonVsPolygon(getTransformedVertices(), polygon.getTransformedVertices()); 94 | 95 | if (result == null) return null; 96 | 97 | if (result.b) { 98 | return new CollisionResult(polygon, this, result.a); 99 | } 100 | 101 | return new CollisionResult(this, polygon, result.a); 102 | } 103 | 104 | override public function testPoint(pointX:FastFloat, pointY:FastFloat):Bool { 105 | if (hasSymmetricalTransformation()) { 106 | return Collision.pointVsCircle(pointX, pointY, matrix._20, matrix._21, radius * matrix._00); 107 | } 108 | 109 | return Collision.pointVsPolygon(pointX, pointY, getTransformedVertices()); 110 | } 111 | 112 | override public function getVertices():Array { 113 | if (!_verticesUpdated) updateVertices(); 114 | return _vertices; 115 | } 116 | 117 | override public function getTransformedVertices():Array { 118 | if (!_verticesUpdated) updateVertices(); 119 | return super.getTransformedVertices(); 120 | } 121 | 122 | /** 123 | * Test this circle with another cirlce using only position & radius, 124 | * ignore other transformation and collision data. 125 | */ 126 | public inline function testCircleNoTransform(circle:CollisionCircle):Bool { 127 | return Collision.fastCircleVsCircle( 128 | matrix.tx, matrix.ty, radius, 129 | circle.matrix.tx, circle.matrix.ty, circle.radius 130 | ); 131 | } 132 | 133 | public function updateVertices():Void { 134 | var segments = this.segments; 135 | if (segments <= 0) { 136 | segments = Math.floor(10 * Math.sqrt(radius)); 137 | } 138 | 139 | if (_vertices.length > segments) _vertices.splice(0, _vertices.length - segments); 140 | 141 | var theta = 2 * Math.PI / segments; 142 | var c = Math.cos(theta); 143 | var s = Math.sin(theta); 144 | 145 | var x = radius; 146 | var y:FastFloat = 0; 147 | var t:FastFloat; 148 | 149 | var point:Vec2; 150 | 151 | for (i in 0...segments) { 152 | if (i < _vertices.length) { 153 | point = _vertices[i]; 154 | } else { 155 | point = new Vec2(); 156 | _vertices.push(point); 157 | } 158 | 159 | point.set(x, y); 160 | 161 | t = x; 162 | x = c * x - s * y; 163 | y = c * y + s * t; 164 | } 165 | 166 | _verticesUpdated = true; 167 | } 168 | 169 | public inline function hasSymmetricalTransformation():Bool { 170 | return matrix._00 == matrix._11 && matrix._10 == 0 && matrix._01 == 0; 171 | } 172 | 173 | function set_radius(value:FastFloat):FastFloat { 174 | _verticesUpdated = false; 175 | return radius = value; 176 | } 177 | 178 | function set_segments(value:Null):Null { 179 | _verticesUpdated = false; 180 | return segments = value; 181 | } 182 | 183 | override function get_width():FastFloat { 184 | return radius * 2; 185 | } 186 | 187 | override function get_height():FastFloat { 188 | return radius * 2; 189 | } 190 | 191 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/transformable/shapes/CollisionPolygon.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.transformable.shapes; 2 | 3 | import kala.behaviors.collision.transformable.CollisionResult; 4 | import kala.math.Collision; 5 | import kala.math.Vec2; 6 | import kala.util.pool.Pool; 7 | import kha.FastFloat; 8 | 9 | class CollisionPolygon extends CollisionShape { 10 | 11 | public static var pool(default, never) = new Pool(function() return new CollisionPolygon()); 12 | 13 | public static inline function get():CollisionPolygon { 14 | var polygon = pool.get(); 15 | polygon.reset(); 16 | return polygon; 17 | } 18 | 19 | // 20 | 21 | public var vertices(get, set):Array; 22 | 23 | var _width:FastFloat; 24 | var _height:FastFloat; 25 | 26 | public function new() { 27 | super(); 28 | isCircle = false; 29 | } 30 | 31 | override public function put():Void { 32 | pool.putUnsafe(this); 33 | } 34 | 35 | override public function testCircle(circle:CollisionCircle):CollisionResult { 36 | var result = circle.testPolygon(this); 37 | if (result == null) return null; 38 | return result.flip(); 39 | } 40 | 41 | override public function testPolygon(polygon:CollisionPolygon):CollisionResult { 42 | var result = Collision.polygonVsPolygon( 43 | getTransformedVertices(), polygon.getTransformedVertices() 44 | ); 45 | 46 | if (result == null) return null; 47 | 48 | if (result.b) { 49 | return new CollisionResult(polygon, this, result.a); 50 | } 51 | 52 | return new CollisionResult(this, polygon, result.a); 53 | } 54 | 55 | override public function testPoint(pointX:FastFloat, pointY:FastFloat):Bool { 56 | return Collision.pointVsPolygon(pointX, pointY, getTransformedVertices()); 57 | } 58 | 59 | function get_vertices():Array { 60 | return _vertices; 61 | } 62 | 63 | function set_vertices(value:Array):Array { 64 | _vertices = value; 65 | 66 | var minX:FastFloat = 0; 67 | var maxX:FastFloat = 0; 68 | var minY:FastFloat = 0; 69 | var maxY:FastFloat = 0; 70 | 71 | for (v in _vertices) { 72 | if (v.x < minX) minX = v.x; 73 | else if (v.x > maxX) maxX = v.x; 74 | 75 | if (v.y < minY) minY = v.y; 76 | else if (v.y > maxY) maxY = v.y; 77 | } 78 | 79 | _width = Math.abs(maxX - minX); 80 | _height = Math.abs(maxY - minY); 81 | 82 | return _vertices; 83 | } 84 | 85 | override function get_width():FastFloat { 86 | return _width; 87 | } 88 | 89 | override function get_height():FastFloat { 90 | return _height; 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /kala/behaviors/collision/transformable/shapes/CollisionShape.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.collision.transformable.shapes; 2 | 3 | import kala.behaviors.collision.BaseCollisionShape; 4 | import kala.math.Matrix; 5 | import kala.math.Position; 6 | import kala.math.Rotation; 7 | import kala.math.Vec2; 8 | import kala.math.Vec2T; 9 | import kha.FastFloat; 10 | 11 | @:access(kala.objects.Object) 12 | @:allow(kala.behaviors.Behavior) 13 | class CollisionShape extends BaseCollisionShape { 14 | 15 | // To avoid using Std.is 16 | public var isCircle(default, null):Bool; 17 | 18 | public var scale:Vec2T = new Vec2T(); 19 | public var rotation:Rotation = new Rotation(); 20 | 21 | public var matrix(default, null):Matrix; 22 | 23 | public var width(get, never):FastFloat; 24 | public var height(get, never):FastFloat; 25 | 26 | private var _vertices:Array = new Array(); 27 | 28 | override public function reset():Void { 29 | super.reset(); 30 | scale.set(1, 1, 0, 0); 31 | rotation.set(0, 0, 0); 32 | matrix = null; 33 | } 34 | 35 | override public function destroy():Void { 36 | super.destroy(); 37 | 38 | scale = null; 39 | rotation = null; 40 | 41 | _vertices = null; 42 | 43 | matrix = null; 44 | } 45 | 46 | override public function update(objectMatrix:Matrix):Void { 47 | matrix = objectMatrix.multmat(Matrix.getTransformation(position, scale, rotation)); 48 | } 49 | 50 | public function getVertices():Array { 51 | return _vertices.copy(); 52 | } 53 | 54 | public function getTransformedVertices():Array { 55 | var transformedVertices = new Array(); 56 | 57 | for (vert in _vertices) { 58 | transformedVertices.push(vert.transform(matrix)); 59 | } 60 | 61 | return transformedVertices; 62 | } 63 | 64 | public function test(shape:CollisionShape):CollisionResult { 65 | if (shape.isCircle) return testCircle(cast shape); 66 | return testPolygon(cast shape); 67 | } 68 | 69 | public function testCircle(circle:CollisionCircle):CollisionResult { 70 | return null; 71 | } 72 | 73 | public function testPolygon(polygon:CollisionPolygon):CollisionResult { 74 | return null; 75 | } 76 | 77 | function get_width():FastFloat { 78 | return 0; 79 | } 80 | 81 | function get_height():FastFloat { 82 | return 0; 83 | } 84 | 85 | override function get_available():Bool { 86 | return matrix != null; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /kala/behaviors/display/Clip.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.display; 2 | 3 | import kala.behaviors.Behavior; 4 | import kala.DrawingData; 5 | import kala.math.Matrix; 6 | import kala.math.Rect.RectI; 7 | import kala.objects.Object; 8 | import kha.Canvas; 9 | 10 | /** 11 | * Used to limit the rendering arena of an object. 12 | * 13 | * Currently does not work correctly on HTML5 target when used on objects that are rendered on a View. 14 | */ 15 | class Clip extends Behavior { 16 | 17 | public var arena(default, null):RectI = new RectI(); 18 | public var absolutePosition:Bool; 19 | public var enable:Bool; 20 | 21 | public function new(?object:Object, x:Int, y:Int, width:Int, height:Int) { 22 | super(object); 23 | arena.set(x, y, width, height); 24 | } 25 | 26 | override public function reset():Void { 27 | super.reset(); 28 | arena.set(); 29 | absolutePosition = false; 30 | enable = true; 31 | } 32 | 33 | override public function addTo(object:Object):Behavior { 34 | super.addTo(object); 35 | 36 | object.onPreDraw.notifyPrivateCB(this, preDrawHandle); 37 | object.onPostDraw.notifyPrivateCB(this, postDrawHandle); 38 | 39 | return this; 40 | } 41 | 42 | override public function remove():Void { 43 | if (object != null) { 44 | object.onPreDraw.removePrivateCB(this, preDrawHandle); 45 | object.onPostDraw.removePrivateCB(this, postDrawHandle); 46 | } 47 | 48 | super.remove(); 49 | } 50 | 51 | function preDrawHandle(obj:Object, data:DrawingData, canvas:Canvas):Bool { 52 | if (enable) { 53 | if (data.extra == null || data.extra.clip == null) { 54 | if (absolutePosition) canvas.g2.scissor(arena.x, arena.y, arena.width, arena.height); 55 | else canvas.g2.scissor(arena.x + Std.int(object.x), arena.y + Std.int(object.y), arena.width, arena.height); 56 | } else { 57 | var absArena = (absolutePosition ? 58 | this.arena : 59 | new RectI(this.arena.x + Std.int(object.x), this.arena.y + Std.int(object.y), this.arena.width, this.arena.height) 60 | ); 61 | 62 | var intersectedArena = absArena.getIntersection(data.extra.clip); 63 | 64 | if (intersectedArena == null) { 65 | canvas.g2.scissor(absArena.x, absArena.y, absArena.width, absArena.height); 66 | if (obj.isGroup) { 67 | if (data.extra == null) data.extra = { clip : absArena }; 68 | else data.extra.clip = absArena; 69 | } 70 | } else { 71 | canvas.g2.scissor(intersectedArena.x, intersectedArena.y, intersectedArena.width, intersectedArena.height); 72 | if (obj.isGroup) { 73 | if (data.extra == null) data.extra = { clip : intersectedArena }; 74 | else data.extra.clip = intersectedArena; 75 | } 76 | } 77 | } 78 | } 79 | 80 | return false; 81 | } 82 | 83 | function postDrawHandle(obj:Object, data:DrawingData, canvas:Canvas):Void { 84 | if (enable) { 85 | #if flash 86 | canvas.g2.scissor(0, 0, -1, -1); 87 | #else 88 | canvas.g2.disableScissor(); 89 | #end 90 | } 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /kala/behaviors/display/Flicker.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.display; 2 | 3 | import kala.behaviors.Behavior; 4 | import kala.objects.Object; 5 | import kha.FastFloat; 6 | 7 | class Flicker extends Behavior { 8 | 9 | public var delay:FastFloat; 10 | public var visibleDuration:FastFloat; 11 | public var flickersLeft:Int; 12 | 13 | public var flickering(get, never):Bool; 14 | 15 | private var _delayTimeLeft:FastFloat; 16 | private var _visibleTimeLeft:FastFloat; 17 | private var _onCompleteCB:Void->Void; 18 | 19 | override public function reset():Void { 20 | super.reset(); 21 | _delayTimeLeft = delay = 1; 22 | visibleDuration = 1; 23 | _visibleTimeLeft = 0; 24 | flickersLeft = 0; 25 | } 26 | 27 | override public function addTo(object:Object):Flicker { 28 | super.addTo(object); 29 | object.onPostUpdate.notifyPrivateCB(this, update); 30 | return this; 31 | } 32 | 33 | override public function remove():Void { 34 | if (object != null) { 35 | object.onPostUpdate.removePrivateCB(this, update); 36 | } 37 | super.remove(); 38 | } 39 | 40 | public inline function flicker(duration:FastFloat, delay:FastFloat = 0, visibleDuration:FastFloat = 0, ?onCompleteCB:Void->Void):Void { 41 | if (delay > 0) this.delay = _delayTimeLeft = delay; 42 | if (visibleDuration > 0) this.visibleDuration = visibleDuration; 43 | flickersLeft = Std.int(duration / (this.delay + this.visibleDuration)); 44 | _visibleTimeLeft = 0; 45 | _onCompleteCB = onCompleteCB; 46 | } 47 | 48 | function update(obj:Object, elapsed:FastFloat):Void { 49 | if (flickersLeft > 0) { 50 | if (_visibleTimeLeft > 0) _visibleTimeLeft -= elapsed; 51 | else { 52 | obj.visible = false; 53 | 54 | if (_delayTimeLeft > 0) _delayTimeLeft -= elapsed; 55 | else { 56 | _delayTimeLeft = delay; 57 | _visibleTimeLeft = visibleDuration; 58 | obj.visible = true; 59 | flickersLeft--; 60 | 61 | if (flickersLeft == 0 && _onCompleteCB != null) { 62 | _onCompleteCB(); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | inline function get_flickering():Bool { 70 | return flickersLeft > 0; 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /kala/behaviors/display/SpriteAnimation.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.display; 2 | 3 | import haxe.ds.StringMap; 4 | import kala.EventHandle.CallbackHandle; 5 | import kala.math.Rect.RectI; 6 | import kala.objects.Object; 7 | import kala.objects.sprite.Sprite; 8 | import kha.FastFloat; 9 | import kha.Image; 10 | 11 | /** 12 | * Used with sprite-sheets to play animation on a sprite object. 13 | */ 14 | @:access(kala.objects.sprite.Sprite) 15 | class SpriteAnimation extends Behavior { 16 | 17 | public var animations(default, null):StringMap = new StringMap(); 18 | 19 | public var anim(default, null):SpriteAnimationData; 20 | public var frame(default, set):Int; 21 | public var delay(default, set):FastFloat; 22 | public var reversed:Bool; 23 | 24 | public var timeLeft:FastFloat; 25 | 26 | public var onAnimComplete(default, null):CallbackHandleVoid>; 27 | 28 | private var _lastAddedKey:String; 29 | 30 | public function new(?sprite:Sprite) { 31 | super(null); 32 | onAnimComplete = addCBHandle(new CallbackHandleVoid>()); 33 | if (sprite != null) addTo(sprite); 34 | } 35 | 36 | override public function reset():Void { 37 | super.reset(); 38 | removeAllAnimations(); 39 | anim = null; 40 | timeLeft = 0; 41 | } 42 | 43 | override public function destroy():Void { 44 | super.destroy(); 45 | 46 | anim = null; 47 | 48 | removeAllAnimations(); 49 | animations = null; 50 | 51 | destroyCBHandles(); 52 | onAnimComplete = null; 53 | } 54 | 55 | override public function addTo(object:Sprite):SpriteAnimation { 56 | super.addTo(object); 57 | 58 | object.onPostUpdate.notifyPrivateCB(this, update); 59 | object.animation = this; 60 | 61 | return this; 62 | } 63 | 64 | override public function remove():Void { 65 | if (object != null) { 66 | object.onPostUpdate.removePrivateCB(this, update); 67 | object.animation = null; 68 | } 69 | 70 | super.remove(); 71 | } 72 | 73 | /** 74 | * Play an animation. 75 | * 76 | * @param key The key used to add the animation. 77 | * @param delay Delay time between frames. Left null to use the current setting. 78 | * @param reversed Whether to play the animation in reverse or not. 79 | * @param forceReplay Whether to force replay the animation if it is already playing with the same settings. 80 | * 81 | * @return This behavior. 82 | */ 83 | public function play(?key:String, ?delay:UInt, reversed:Bool = false, forceReplay:Bool = false):SpriteAnimation { 84 | if (key == null) { 85 | if (_lastAddedKey == null) return null; 86 | key = _lastAddedKey; 87 | } 88 | 89 | if ( 90 | anim != null && key == anim.key && !forceReplay && 91 | (delay == null || delay == this.delay) && 92 | reversed == this.reversed 93 | ) { 94 | return this; 95 | } 96 | 97 | anim = animations.get(key); 98 | 99 | if (anim != null) { 100 | this.delay = timeLeft = (delay == null) ? anim.delay : delay; 101 | this.reversed = reversed; 102 | frame = reversed ? anim.frames.length - 1 : 0; 103 | if (anim.image != null) object.image = anim.image; 104 | } else { 105 | return null; 106 | } 107 | 108 | return this; 109 | } 110 | 111 | public inline function pause():Void { 112 | delay = -1; 113 | } 114 | 115 | /** 116 | * Add a new animation. 117 | * 118 | * @param key Key used to access animation. 119 | * @param image Source image contains sprite sheet. If set to null, will use the current image of the owner sprite (set by sprite.loadImage or most preview calling of addAnim). If this argument is null and the behavior wasn't added to a sprite or the sprite image is null, this method will do nothing and return null. 120 | * @param sheetX X position of sprite sheet. If set to smaller than 0, will use the current frame x of the owner sprite (set by sprite.loadImage or most preview calling of addAnim). If this argument is set to smaller than 0 and the behavior wasn't added to a sprite, this method will do nothing and return null. 121 | * @param sheetY Y position of sprite sheet. If set to smaller than 0, will use the current frame y of the owner sprite (set by sprite.loadImage or most preview calling of addAnim). If this argument is set to to smaller than 0 and the behavior wasn't added to a sprite, this method will do nothing and return null. 122 | * @param frameWidth Frame width. If set to 0, will use the current frame width of the owner sprite (set by sprite.loadImage or most preview calling of addAnim). If this argument is set to 0 and the behavior wasn't added to a sprite, this method will do nothing and return null. 123 | * @param frameHeight Frame height. If set to 0, will use the current frame height of the owner sprite (set by sprite.loadImage or most preview calling of addAnim). If this argument is set to 0 and the behavior wasn't added to a sprite, this method will do nothing and return null. 124 | * @param totalFrames Total number of frames in sprite sheet. 125 | * @param framesPerRow Number of frames per row. (Last row may have less frames.) 126 | * @param delay Delay time between frames. In seconds if Kala.deltaTiming is set to true otherwise in frames. 127 | * 128 | * @return Return this behavior if success otherwise return null. 129 | */ 130 | public function addAnim( 131 | key:String, 132 | ?image:Image, 133 | sheetX:Int, sheetY:Int, frameWidth:UInt, frameHeight:UInt, 134 | totalFrames:UInt, 135 | framesPerRow:UInt, 136 | delay:UInt 137 | ):SpriteAnimation { 138 | if ( 139 | (image == null && (object == null || object.image == null)) || 140 | (object == null && (sheetX < 0 || sheetY < 0 || frameWidth == 0 || frameHeight == 0)) 141 | ) { 142 | return null; 143 | } 144 | 145 | if (image == null) image = object.image; 146 | else object.image = image; 147 | 148 | if (sheetX < 0) sheetX = object.frameRect.x; 149 | else object.frameRect.x = sheetX; 150 | 151 | if (sheetY < 0) sheetY = object.frameRect.y; 152 | else object.frameRect.y = sheetY; 153 | 154 | if (frameWidth == 0) frameWidth = object.frameRect.width; 155 | else object.frameRect.width = frameWidth; 156 | 157 | if (frameHeight == 0) frameHeight = object.frameRect.height; 158 | else object.frameRect.height = frameHeight; 159 | 160 | var anim = new SpriteAnimationData(key, image, delay); 161 | 162 | var col:UInt = 0; 163 | var row = 0; 164 | for (i in 0...totalFrames) { 165 | anim.addFrame(sheetX + frameWidth * col, sheetY + frameHeight * row, frameWidth, frameHeight); 166 | 167 | col++; 168 | 169 | if (col == framesPerRow) { 170 | col = 0; 171 | row++; 172 | } 173 | } 174 | 175 | animations.set(key, anim); 176 | _lastAddedKey = key; 177 | 178 | return this; 179 | } 180 | 181 | public function addAnimFromSpriteData(?key:String, ?image:Image, data:SpriteData, delay:Int):SpriteAnimation { 182 | if (key == null) key = data.key; 183 | 184 | if (image == null) { 185 | if (data.image == null) { 186 | image = object.image; 187 | } else { 188 | image = data.image; 189 | } 190 | } 191 | 192 | object.image = image; 193 | object.frameRect.copy(data.frames[0]); 194 | 195 | var anim = new SpriteAnimationData(key, image, delay); 196 | 197 | for (frame in data.frames) { 198 | anim.addFrameRect(frame); 199 | } 200 | 201 | animations.set(key, anim); 202 | _lastAddedKey = key; 203 | 204 | return this; 205 | } 206 | 207 | public function removeAnim(key:String):SpriteAnimation { 208 | animations.remove(key); 209 | if (_lastAddedKey == key) _lastAddedKey = null; 210 | 211 | return this; 212 | } 213 | 214 | public function getAnimations():Array { 215 | var array = new Array(); 216 | for (key in animations.keys()) { 217 | array.push(animations.get(key)); 218 | } 219 | return array; 220 | } 221 | 222 | public function removeAllAnimations():Void { 223 | for (key in animations.keys()) animations.remove(key); 224 | } 225 | 226 | function update(obj:Object, elapsed:FastFloat):Void { 227 | if (anim != null && delay > -1) { 228 | timeLeft -= elapsed; 229 | 230 | if (timeLeft <= 0) { 231 | timeLeft = delay; 232 | 233 | if (!reversed) { 234 | if (frame < anim.frames.length - 1) { 235 | frame++; 236 | } else { 237 | frame = 0; 238 | for (callback in onAnimComplete) callback.cbFunction(this); 239 | } 240 | } else { 241 | if (frame > 0) { 242 | frame--; 243 | } else { 244 | frame = anim.frames.length - 1; 245 | for (callback in onAnimComplete) callback.cbFunction(this); 246 | } 247 | } 248 | 249 | } 250 | } 251 | } 252 | 253 | inline function set_frame(value:Int):Int { 254 | object.frameRect.copy(anim.frames[frame = value]); 255 | return value; 256 | } 257 | 258 | inline function set_delay(value:FastFloat):FastFloat { 259 | if (value > -1 && timeLeft > value) timeLeft = value; 260 | return delay = value; 261 | } 262 | 263 | } 264 | 265 | class SpriteAnimationData { 266 | 267 | public var key(default, null):String; 268 | public var image:Image; 269 | public var frames:Array; 270 | public var delay:FastFloat; 271 | 272 | public inline function new(key:String, image:Image, delay:UInt) { 273 | this.key = key; 274 | this.image = image; 275 | this.delay = delay; 276 | 277 | frames = new Array(); 278 | } 279 | 280 | @:extern 281 | public inline function addFrame(x:UInt, y:UInt, width:UInt, height:UInt):SpriteAnimationData { 282 | frames.push(new RectI(x, y, width, height)); 283 | return this; 284 | } 285 | 286 | @:extern 287 | public inline function addFrameRect(rect:RectI):SpriteAnimationData { 288 | frames.push(rect.clone()); 289 | return this; 290 | } 291 | 292 | @:extern 293 | public inline function removeFrame(index:Int):SpriteAnimationData { 294 | frames.splice(index, 1); 295 | return this; 296 | } 297 | 298 | } -------------------------------------------------------------------------------- /kala/behaviors/input/BaseButtonInteraction.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.input; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.EventHandle.CallbackHandle; 5 | import kala.math.Vec2; 6 | import kala.objects.group.View; 7 | import kala.objects.Object; 8 | import kha.FastFloat; 9 | 10 | class BaseButtonInteraction extends Behavior { 11 | 12 | public var active:Bool; 13 | public var hovered(default, null):Bool; 14 | public var pushed(get, never):Bool; 15 | 16 | /** 17 | * The second arg is id of touch or mouse button (1 - left, 2 - middle, 3 - right). 18 | */ 19 | public var onPush(default, null):CallbackHandleInt->Void>; 20 | /** 21 | * The second arg is id of touch or mouse button (1 - left, 2 - middle, 3 - right). 22 | */ 23 | public var onRelease(default, null):CallbackHandleInt->Void>; 24 | public var onOver(default, null):CallbackHandleVoid>; 25 | public var onOut(default, null):CallbackHandleVoid>; 26 | 27 | public var onPushRequestFullscreen:Bool; 28 | public var onReleaseRequestFullscreen:Bool; 29 | public var onPushOpenURL:String; 30 | public var onReleaseOpenURL:String; 31 | 32 | public var view:View; 33 | 34 | #if js 35 | public var disableMouseOnMobile:Bool; 36 | #end 37 | 38 | private var _mouseHovered:Bool; 39 | private var _touched:Bool; 40 | 41 | public function new(?object:Object, objectRectScale:FastFloat = 0) { 42 | super(object); 43 | 44 | onPush = addCBHandle(new CallbackHandleInt->Void>()); 45 | onRelease = addCBHandle(new CallbackHandleInt->Void>()); 46 | onOver = addCBHandle(new CallbackHandleVoid>()); 47 | onOut = addCBHandle(new CallbackHandleVoid>()); 48 | } 49 | 50 | override public function reset():Void { 51 | super.reset(); 52 | active = true; 53 | hovered = _mouseHovered = _touched = false; 54 | #if js 55 | disableMouseOnMobile = true; 56 | #end 57 | view = null; 58 | onPushRequestFullscreen = onReleaseRequestFullscreen = false; 59 | onPushOpenURL = onReleaseOpenURL = null; 60 | } 61 | 62 | override public function destroy():Void { 63 | super.destroy(); 64 | view = null; 65 | onPush = null; 66 | onRelease = null; 67 | onOver = null; 68 | onOut = null; 69 | } 70 | 71 | override public function addTo(object:Object):BaseButtonInteraction { 72 | super.addTo(object); 73 | object.onPostUpdate.notifyPrivateCB(this, update); 74 | return this; 75 | } 76 | 77 | override public function remove():Void { 78 | if (object != null) { 79 | object.onPostUpdate.removePrivateCB(this, update); 80 | } 81 | 82 | super.remove(); 83 | } 84 | 85 | function test(x:FastFloat, y:FastFloat):Bool { 86 | return false; 87 | } 88 | 89 | function update(obj:Object, elapsed:FastFloat):Void { 90 | if (!active) return; 91 | 92 | #if kala_mouse 93 | #if js 94 | if (!disableMouseOnMobile || !Kala.html5.mobile) updateMouse(); 95 | #else 96 | updateMouse(); 97 | #end 98 | #end 99 | 100 | #if kala_touch 101 | updateTouch(); 102 | #end 103 | 104 | if (hovered && !_touched && !_mouseHovered) { 105 | callOnOut(); 106 | } 107 | } 108 | 109 | #if kala_mouse 110 | function updateMouse():Void { 111 | var p:Vec2; 112 | if (view == null) p = new Vec2(kala.input.Mouse.x, kala.input.Mouse.y); 113 | else p = view.project(kala.input.Mouse.x, kala.input.Mouse.y); 114 | 115 | if (test(p.x, p.y)) { 116 | if (!hovered) { 117 | callOnOver(); 118 | } 119 | 120 | if (kala.input.Mouse.LEFT.justPressed) { 121 | callOnPush(1); 122 | } else if (kala.input.Mouse.LEFT.justReleased) { 123 | callOnRelease(1); 124 | } 125 | 126 | if (kala.input.Mouse.MIDDLE.justPressed) { 127 | callOnPush(2); 128 | } else if (kala.input.Mouse.MIDDLE.justReleased) { 129 | callOnRelease(2); 130 | } 131 | 132 | if (kala.input.Mouse.RIGHT.justPressed) { 133 | callOnPush(3); 134 | } else if (kala.input.Mouse.RIGHT.justReleased) { 135 | callOnRelease(3); 136 | } 137 | 138 | _mouseHovered = true; 139 | } else { 140 | _mouseHovered = false; 141 | } 142 | } 143 | #end 144 | 145 | #if kala_touch 146 | function updateTouch():Void { 147 | _touched = false; 148 | 149 | var p:Vec2; 150 | 151 | for (touch in kala.input.Touch.touches) { 152 | if (view == null) p = new Vec2(touch.x, touch.y); 153 | else p = view.project(touch.x, touch.y); 154 | 155 | if (test(p.x, p.y)) { 156 | if (!hovered) { 157 | callOnOver(); 158 | } 159 | 160 | if (touch.justStarted) { 161 | callOnPush(touch.id); 162 | } else if (touch.justEnded) { 163 | callOnRelease(touch.id); 164 | } 165 | 166 | _touched = true; 167 | } 168 | } 169 | } 170 | #end 171 | 172 | inline function callOnOver():Void { 173 | #if (flash || js) 174 | if (onPushOpenURL != null) { 175 | #if js 176 | Kala.html5.canvas.addEventListener('click', openURLOnPush); 177 | #elseif flash 178 | #end 179 | } 180 | 181 | if (onReleaseOpenURL != null) { 182 | #if js 183 | Kala.html5.canvas.addEventListener('mouseup', openURLOnRelease); 184 | #elseif flash 185 | #end 186 | } 187 | 188 | if (onPushRequestFullscreen) { 189 | #if js 190 | Kala.html5.canvas.addEventListener('click', requestFullscreen); 191 | #elseif flash 192 | #end 193 | } 194 | 195 | if (onReleaseRequestFullscreen) { 196 | #if js 197 | Kala.html5.canvas.addEventListener('mouseup', requestFullscreen); 198 | #elseif flash 199 | #end 200 | } 201 | #end 202 | 203 | hovered = true; 204 | for (callback in onOver) callback.cbFunction(this); 205 | } 206 | 207 | inline function callOnOut():Void { 208 | #if js 209 | Kala.html5.canvas.removeEventListener('click', openURLOnPush); 210 | Kala.html5.canvas.removeEventListener('mouseup', openURLOnRelease); 211 | Kala.html5.canvas.removeEventListener('click', requestFullscreen); 212 | Kala.html5.canvas.removeEventListener('mouseup', requestFullscreen); 213 | #elseif flash 214 | #end 215 | 216 | hovered = false; 217 | for (callback in onOut) callback.cbFunction(this); 218 | } 219 | 220 | inline function callOnPush(id:Int):Void { 221 | #if (!flash && !js) 222 | if (onPushOpenURL != null) Kala.openURL(onPushOpenURL); 223 | if (onPushRequestFullscreen) Kala.requestFullscreen(); 224 | #end 225 | 226 | for (callback in onPush) callback.cbFunction(this, id); 227 | } 228 | 229 | inline function callOnRelease(id:Int):Void { 230 | #if (!flash && !js) 231 | if (onReleaseOpenURL != null) Kala.openURL(onReleaseOpenURL); 232 | if (onReleaseRequestFullscreen) Kala.requestFullscreen(); 233 | #end 234 | 235 | for (callback in onRelease) callback.cbFunction(this, id); 236 | } 237 | 238 | #if (flash || js) 239 | function requestFullscreen():Void { 240 | Kala.requestFullscreen(); 241 | } 242 | 243 | function openURLOnPush():Void { 244 | Kala.openURL(onPushOpenURL); 245 | } 246 | 247 | function openURLOnRelease():Void { 248 | Kala.openURL(onReleaseOpenURL); 249 | } 250 | #end 251 | 252 | function get_pushed():Bool { 253 | return _touched || _mouseHovered; 254 | } 255 | 256 | } 257 | #end -------------------------------------------------------------------------------- /kala/behaviors/input/BasicButtonInteraction.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.input; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.behaviors.collision.basic.shapes.CollisionCircle; 5 | import kala.behaviors.collision.basic.shapes.CollisionRectangle; 6 | import kala.behaviors.collision.basic.Collider; 7 | import kala.objects.Object; 8 | import kha.FastFloat; 9 | 10 | class BasicButtonInteraction extends BaseButtonInteraction { 11 | 12 | public var collider:Collider; 13 | 14 | public function new(?object:Object, ?collider:Collider, objectRectScale:FastFloat = 0) { 15 | super(null, objectRectScale); 16 | 17 | this.collider = collider; 18 | 19 | if (object != null) { 20 | addTo(object); 21 | if (objectRectScale > 0) addObjectRectMask(); 22 | } 23 | } 24 | 25 | override public function reset():Void { 26 | super.reset(); 27 | if (collider != null) collider.reset(); 28 | } 29 | 30 | override public function destroy():Void { 31 | super.destroy(); 32 | collider = null; 33 | } 34 | 35 | override public function addTo(object:Object):BasicButtonInteraction { 36 | super.addTo(object); 37 | if (collider == null) collider = new Collider(object); 38 | else collider.addTo(object); 39 | return this; 40 | } 41 | 42 | override public function remove():Void { 43 | if (object != null) { 44 | collider.remove(); 45 | } 46 | 47 | super.remove(); 48 | } 49 | 50 | public inline function addCircleMask(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 51 | return collider.addCircle(x, y, radius); 52 | } 53 | 54 | public inline function addRectMask(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionRectangle { 55 | return collider.addRect(x, y, width, height); 56 | } 57 | 58 | public inline function addObjectRectMask():CollisionRectangle { 59 | return collider.addObjectRect(); 60 | } 61 | 62 | override function test(x:FastFloat, y:FastFloat):Bool { 63 | return collider.testPoint(x, y); 64 | } 65 | 66 | } 67 | 68 | #end -------------------------------------------------------------------------------- /kala/behaviors/input/ButtonInteraction.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.input; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.behaviors.collision.transformable.shapes.CollisionCircle; 5 | import kala.behaviors.collision.transformable.shapes.CollisionPolygon; 6 | import kala.behaviors.collision.transformable.shapes.CollisionShape; 7 | import kala.behaviors.collision.transformable.Collider; 8 | import kala.math.Vec2; 9 | import kala.objects.Object; 10 | import kha.FastFloat; 11 | 12 | class ButtonInteraction extends BaseButtonInteraction { 13 | 14 | public var collider:Collider; 15 | 16 | public function new(?object:Object, ?collider:Collider, objectRectScale:FastFloat = 0) { 17 | super(null, objectRectScale); 18 | 19 | this.collider = collider; 20 | 21 | if (object != null) { 22 | addTo(object); 23 | if (objectRectScale > 0) addObjectRectMask(); 24 | } 25 | } 26 | 27 | override public function reset():Void { 28 | super.reset(); 29 | if (collider != null) collider.reset(); 30 | } 31 | 32 | override public function destroy():Void { 33 | super.destroy(); 34 | collider = null; 35 | } 36 | 37 | override public function addTo(object:Object):ButtonInteraction { 38 | super.addTo(object); 39 | if (collider == null) collider = new Collider(object); 40 | else collider.addTo(object); 41 | return this; 42 | } 43 | 44 | override public function remove():Void { 45 | if (object != null) { 46 | collider.remove(); 47 | } 48 | 49 | super.remove(); 50 | } 51 | 52 | public inline function addCircleMask(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 53 | return collider.addCircle(x, y, radius); 54 | } 55 | 56 | public inline function addRectMask(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionPolygon { 57 | return collider.addRect(x, y, width, height); 58 | } 59 | 60 | public inline function addPolygonMask(x:FastFloat, y:FastFloat, vertices:Array, concave:Bool = false):Array { 61 | return collider.addPolygon(x, y, vertices, concave); 62 | } 63 | 64 | public inline function addObjectRectMask():CollisionPolygon { 65 | return collider.addObjectRect(); 66 | } 67 | 68 | public inline function addShapeMask(shape:CollisionShape):CollisionShape { 69 | return collider.addShape(shape); 70 | } 71 | 72 | override function test(x:FastFloat, y:FastFloat):Bool { 73 | return collider.testPoint(x, y); 74 | } 75 | 76 | } 77 | 78 | #end -------------------------------------------------------------------------------- /kala/behaviors/input/MouseInteraction.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.input; 2 | 3 | #if kala_mouse 4 | 5 | import kala.behaviors.collision.transformable.Collider; 6 | import kala.behaviors.collision.transformable.shapes.CollisionCircle; 7 | import kala.behaviors.collision.transformable.shapes.CollisionPolygon; 8 | import kala.behaviors.collision.transformable.shapes.CollisionShape; 9 | import kala.behaviors.Behavior; 10 | import kala.EventHandle.CallbackHandle; 11 | import kala.math.color.Color; 12 | import kala.math.Vec2; 13 | import kala.input.Mouse; 14 | import kala.objects.group.View; 15 | import kala.objects.Object; 16 | import kha.FastFloat; 17 | 18 | class MouseInteraction extends Behavior { 19 | 20 | public var collider:Collider; 21 | 22 | public var active:Bool; 23 | public var hovered(default, null):Bool; 24 | 25 | public var left(default, null):MouseInteractionInput = new MouseInteractionInput(MouseButton.LEFT); 26 | public var right(default, null):MouseInteractionInput = new MouseInteractionInput(MouseButton.RIGHT); 27 | public var middle(default, null):MouseInteractionInput = new MouseInteractionInput(MouseButton.MIDDLE); 28 | 29 | public var onButtonInput(default, null):CallbackHandleMouseInteractionInput->Void>; 30 | public var onWheel(default, null):CallbackHandleInt->Void>; 31 | public var onOver(default, null):CallbackHandleVoid>; 32 | public var onOut(default, null):CallbackHandleVoid>; 33 | 34 | public var dragable:Bool; 35 | public var dragButtons:Array = [MouseButton.LEFT]; 36 | public var dragging:Bool; 37 | 38 | public var view:View; 39 | 40 | private var _dragPointX:FastFloat; 41 | private var _dragPointY:FastFloat; 42 | 43 | public function new(?object:Object, ?collider:Collider, objectRectScale:FastFloat = 0) { 44 | super(); 45 | 46 | this.collider = collider; 47 | 48 | onButtonInput = addCBHandle(new CallbackHandleMouseInteractionInput->Void>()); 49 | onWheel = addCBHandle(new CallbackHandleInt->Void>()); 50 | onOver = addCBHandle(new CallbackHandleVoid>()); 51 | onOut = addCBHandle(new CallbackHandleVoid>()); 52 | 53 | if (object != null) { 54 | addTo(object); 55 | if (objectRectScale > 0) addObjectRectMask(); 56 | } 57 | } 58 | 59 | override public function reset():Void { 60 | super.reset(); 61 | active = true; 62 | hovered = false; 63 | dragable = false; 64 | dragging = false; 65 | view = null; 66 | if (collider != null) collider.reset(); 67 | } 68 | 69 | override public function destroy():Void { 70 | super.destroy(); 71 | 72 | collider.destroy(); 73 | collider = null; 74 | 75 | left = right = middle = null; 76 | 77 | dragButtons = null; 78 | 79 | destroyCBHandles(); 80 | onButtonInput = null; 81 | onWheel = null; 82 | onOver = null; 83 | onOut = null; 84 | 85 | view = null; 86 | } 87 | 88 | override public function addTo(object:Object):MouseInteraction { 89 | super.addTo(object); 90 | if (collider == null) collider = new Collider(object); 91 | else collider.addTo(object); 92 | object.onPostUpdate.notifyPrivateCB(this, update); 93 | return this; 94 | } 95 | 96 | override public function remove():Void { 97 | if (object != null) { 98 | collider.remove(); 99 | object.onPostUpdate.removePrivateCB(this, update); 100 | } 101 | 102 | super.remove(); 103 | } 104 | 105 | public inline function addCircleMask(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 106 | return collider.addCircle(x, y, radius); 107 | } 108 | 109 | public inline function addRectMask(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionPolygon { 110 | return collider.addRect(x, y, width, height); 111 | } 112 | 113 | public inline function addPolygonMask(x:FastFloat, y:FastFloat, vertices:Array, concave:Bool = false):Array { 114 | return collider.addPolygon(x, y, vertices, concave); 115 | } 116 | 117 | public inline function addObjectRectMask():CollisionPolygon { 118 | return collider.addObjectRect(); 119 | } 120 | 121 | public inline function addShapeMask(shape:CollisionShape):CollisionShape { 122 | return collider.addShape(shape); 123 | } 124 | 125 | function update(obj:Object, elapsed:FastFloat):Void { 126 | if (!active) return; 127 | 128 | var m:Vec2; 129 | 130 | if (view == null) m = new Vec2(Mouse.x, Mouse.y); 131 | else m = view.project(Mouse.x, Mouse.y); 132 | 133 | if (collider.testPoint(m.x, m.y)) { 134 | if (!hovered) { 135 | hovered = true; 136 | for (callback in onOver) callback.cbFunction(this); 137 | } 138 | 139 | if (Mouse.LEFT.pressed) { 140 | if (left.duration == -1) { 141 | left.duration = 0; 142 | left.clicked = Mouse.LEFT.justPressed; 143 | } else { 144 | left.duration += elapsed; 145 | left.clicked = false; 146 | } 147 | 148 | left.pressed = true; 149 | 150 | dispatchButtonInput(left); 151 | 152 | } else { 153 | if (left.duration > -1) { 154 | left.duration = -1; 155 | left.justReleased = true; 156 | dispatchButtonInput(left); 157 | } else { 158 | left.justReleased = false; 159 | } 160 | 161 | left.pressed = false; 162 | } 163 | 164 | if (Mouse.RIGHT.pressed) { 165 | if (right.duration == -1) { 166 | right.duration = 0; 167 | right.clicked = Mouse.RIGHT.justPressed; 168 | } else { 169 | right.duration += elapsed; 170 | right.clicked = false; 171 | } 172 | 173 | right.pressed = true; 174 | 175 | dispatchButtonInput(right); 176 | 177 | } else { 178 | if (right.duration > -1) { 179 | right.duration = -1; 180 | right.justReleased = true; 181 | dispatchButtonInput(right); 182 | } else { 183 | right.justReleased = false; 184 | } 185 | 186 | right.pressed = false; 187 | } 188 | 189 | if (Mouse.MIDDLE.pressed) { 190 | if (middle.duration == -1) { 191 | middle.duration = 0; 192 | middle.clicked = Mouse.MIDDLE.justPressed; 193 | } else { 194 | middle.duration += elapsed; 195 | middle.clicked = false; 196 | } 197 | 198 | middle.pressed = true; 199 | 200 | dispatchButtonInput(middle); 201 | 202 | } else { 203 | if (middle.duration > -1) { 204 | middle.duration = -1; 205 | middle.justReleased = true; 206 | dispatchButtonInput(middle); 207 | } else { 208 | middle.justReleased = false; 209 | } 210 | 211 | middle.pressed = false; 212 | } 213 | 214 | if (Mouse.wheel != 0) { 215 | for (callback in onWheel) callback.cbFunction(this, Mouse.wheel); 216 | } 217 | 218 | if (dragButtons != null && Mouse.checkAnyJustPressed(dragButtons)) { 219 | _dragPointX = m.x - obj.x; 220 | _dragPointY = m.y - obj.y; 221 | dragging = true; 222 | } 223 | } else { 224 | if (hovered) { 225 | for (callback in onOver) { 226 | hovered = false; 227 | for (callback in onOut) callback.cbFunction(this); 228 | } 229 | } 230 | 231 | if (left.resetOnMovingOut) left.duration = -1; 232 | if (right.resetOnMovingOut) right.duration = -1; 233 | if (middle.resetOnMovingOut) middle.duration = -1; 234 | 235 | left.pressed = right.pressed = middle.pressed = false; 236 | } 237 | 238 | if (dragable && dragging && dragButtons != null && Mouse.checkAnyPressed(dragButtons)) { 239 | obj.x = m.x - _dragPointX; 240 | obj.y = m.y - _dragPointY; 241 | } else { 242 | dragging = false; 243 | } 244 | } 245 | 246 | inline function dispatchButtonInput(input:MouseInteractionInput):Void { 247 | for (callback in onButtonInput) callback.cbFunction(this, input); 248 | } 249 | 250 | } 251 | 252 | @:allow(kala.behaviors.input.MouseInteraction) 253 | class MouseInteractionInput { 254 | 255 | public var button(default, null):MouseButton; 256 | 257 | public var pressed(default, null):Bool; 258 | public var clicked(default, null):Bool; 259 | public var justReleased(default, null):Bool; 260 | 261 | /** 262 | * The time the button has been pressed on the behavior. 263 | */ 264 | public var duration(default, null):FastFloat = -1; 265 | 266 | /** 267 | * Whether to reset duration when cursor is moving out of the behavior or not. 268 | */ 269 | public var resetOnMovingOut:Bool = true; 270 | 271 | inline function new(button:MouseButton) { 272 | this.button = button; 273 | } 274 | 275 | } 276 | 277 | #end -------------------------------------------------------------------------------- /kala/behaviors/motion/VelocityMotion.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.motion; 2 | 3 | import kala.behaviors.Behavior; 4 | import kala.math.Vec2; 5 | import kala.math.Velocity; 6 | import kala.objects.Object; 7 | import kha.FastFloat; 8 | 9 | class VelocityMotion extends Behavior { 10 | 11 | public var velocity:Velocity = new Velocity(); 12 | public var accelXY:Velocity = new Velocity(); 13 | public var accel:FastFloat; 14 | public var turnSpeed:FastFloat; 15 | public var turnAccel:FastFloat; 16 | 17 | override public function reset():Void { 18 | super.reset(); 19 | velocity.set(); 20 | accelXY.set(); 21 | accel = 0; 22 | turnSpeed = 0; 23 | turnAccel = 0; 24 | } 25 | 26 | override public function destroy():Void { 27 | super.destroy(); 28 | velocity = null; 29 | accelXY = null; 30 | } 31 | 32 | override public function addTo(object:Object):VelocityMotion { 33 | super.addTo(object); 34 | object.onPostUpdate.notifyPrivateCB(this, update); 35 | return this; 36 | } 37 | 38 | override public function remove():Void { 39 | if (object != null) { 40 | object.onPostUpdate.removePrivateCB(this, update); 41 | } 42 | super.remove(); 43 | } 44 | 45 | function update(obj:Object, elapsed:FastFloat):Void { 46 | var factor = elapsed; 47 | if (Kala.deltaTiming) factor /= Kala.perfectDelta; 48 | 49 | if (accelXY.x != 0) velocity.x += accelXY.x * factor; 50 | if (accelXY.y != 0) velocity.y += accelXY.y * factor; 51 | if (accel != 0) velocity.speed += accel * factor; 52 | 53 | turnSpeed += turnAccel * factor; 54 | if (turnSpeed != 0) velocity.angle += turnSpeed * factor; 55 | 56 | obj.x += velocity.x * factor; 57 | obj.y += velocity.y * factor; 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /kala/behaviors/tween/Ease.hx: -------------------------------------------------------------------------------- 1 | package kala.behaviors.tween; 2 | 3 | import kha.FastFloat; 4 | 5 | // The codes below were copied and modified from https://github.com/HaxeFlixel/flixel/blob/dev/flixel/tweens/FlxEase.hx 6 | 7 | /* 8 | * Copyright (c) 2009 Adam 'Atomic' Saltsman 9 | * Copyright (c) 2012 Matt Tuttle 10 | * Copyright (c) 2013 HaxeFlixel Team 11 | 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 13 | * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 15 | * is furnished to do so, subject to the following conditions: 16 | 17 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 18 | 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 20 | * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * Static class with useful easer functions that can be used by Tweens. 27 | * 28 | * Operation of in/out easers: 29 | * 30 | * in(t) 31 | * return t; 32 | * out(t) 33 | * return 1 - in(1 - t); 34 | * inOut(t) 35 | * return (t <= .5) ? in(t * 2) / 2 : out(t * 2 - 1) / 2 + .5; 36 | */ 37 | class Ease { 38 | 39 | /** 40 | * Easing constants. 41 | */ 42 | private static var PI2:FastFloat = Math.PI / 2; 43 | private static var EL:FastFloat = 2 * Math.PI / .45; 44 | private static var B1:FastFloat = 1 / 2.75; 45 | private static var B2:FastFloat = 2 / 2.75; 46 | private static var B3:FastFloat = 1.5 / 2.75; 47 | private static var B4:FastFloat = 2.5 / 2.75; 48 | private static var B5:FastFloat = 2.25 / 2.75; 49 | private static var B6:FastFloat = 2.625 / 2.75; 50 | private static var ELASTIC_AMPLITUDE:FastFloat = 1; 51 | private static var ELASTIC_PERIOD:FastFloat = 0.4; 52 | 53 | public static inline function linear(t:FastFloat):FastFloat { 54 | return t; 55 | } 56 | 57 | // Quadratic 58 | 59 | public static inline function quadIn(t:FastFloat):FastFloat { 60 | return t * t; 61 | } 62 | 63 | public static inline function quadOut(t:FastFloat):FastFloat { 64 | return -t * (t - 2); 65 | } 66 | 67 | public static inline function quadInOut(t:FastFloat):FastFloat { 68 | return t <= .5 ? t * t * 2 : 1 - (--t) * t * 2; 69 | } 70 | 71 | // Cubic 72 | 73 | public static inline function cubeIn(t:FastFloat):FastFloat { 74 | return t * t * t; 75 | } 76 | 77 | public static inline function cubeOut(t:FastFloat):FastFloat { 78 | return 1 + (--t) * t * t; 79 | } 80 | 81 | public static inline function cubeInOut(t:FastFloat):FastFloat { 82 | return t <= .5 ? t * t * t * 4 : 1 + (--t) * t * t * 4; 83 | } 84 | 85 | // Quartic 86 | 87 | public static inline function quartIn(t:FastFloat):FastFloat { 88 | return t * t * t * t; 89 | } 90 | 91 | public static inline function quartOut(t:FastFloat):FastFloat { 92 | return 1 - (t -= 1) * t * t * t; 93 | } 94 | 95 | public static inline function quartInOut(t:FastFloat):FastFloat { 96 | return t <= .5 ? t * t * t * t * 8 : (1 - (t = t * 2 - 2) * t * t * t) / 2 + .5; 97 | } 98 | 99 | // Quintic 100 | 101 | public static inline function quintIn(t:FastFloat):FastFloat { 102 | return t * t * t * t * t; 103 | } 104 | 105 | public static inline function quintOut(t:FastFloat):FastFloat { 106 | return (t = t - 1) * t * t * t * t + 1; 107 | } 108 | 109 | public static inline function quintInOut(t:FastFloat):FastFloat { 110 | return ((t *= 2) < 1) ? (t * t * t * t * t) / 2 : ((t -= 2) * t * t * t * t + 2) / 2; 111 | } 112 | 113 | // Sinusoidal 114 | 115 | public static inline function sineIn(t:FastFloat):FastFloat { 116 | return -Math.cos(PI2 * t) + 1; 117 | } 118 | 119 | public static inline function sineOut(t:FastFloat):FastFloat { 120 | return Math.sin(PI2 * t); 121 | } 122 | 123 | public static inline function sineInOut(t:FastFloat):FastFloat { 124 | return -Math.cos(Math.PI * t) / 2 + .5; 125 | } 126 | 127 | // Bounce 128 | 129 | public static function bounceIn(t:FastFloat):FastFloat { 130 | t = 1 - t; 131 | if (t < B1) return 1 - 7.5625 * t * t; 132 | if (t < B2) return 1 - (7.5625 * (t - B3) * (t - B3) + .75); 133 | if (t < B4) return 1 - (7.5625 * (t - B5) * (t - B5) + .9375); 134 | return 1 - (7.5625 * (t - B6) * (t - B6) + .984375); 135 | } 136 | 137 | public static function bounceOut(t:FastFloat):FastFloat { 138 | if (t < B1) return 7.5625 * t * t; 139 | if (t < B2) return 7.5625 * (t - B3) * (t - B3) + .75; 140 | if (t < B4) return 7.5625 * (t - B5) * (t - B5) + .9375; 141 | return 7.5625 * (t - B6) * (t - B6) + .984375; 142 | } 143 | 144 | public static function bounceInOut(t:FastFloat):FastFloat { 145 | if (t < .5) 146 | { 147 | t = 1 - t * 2; 148 | if (t < B1) return (1 - 7.5625 * t * t) / 2; 149 | if (t < B2) return (1 - (7.5625 * (t - B3) * (t - B3) + .75)) / 2; 150 | if (t < B4) return (1 - (7.5625 * (t - B5) * (t - B5) + .9375)) / 2; 151 | return (1 - (7.5625 * (t - B6) * (t - B6) + .984375)) / 2; 152 | } 153 | t = t * 2 - 1; 154 | if (t < B1) return (7.5625 * t * t) / 2 + .5; 155 | if (t < B2) return (7.5625 * (t - B3) * (t - B3) + .75) / 2 + .5; 156 | if (t < B4) return (7.5625 * (t - B5) * (t - B5) + .9375) / 2 + .5; 157 | return (7.5625 * (t - B6) * (t - B6) + .984375) / 2 + .5; 158 | } 159 | 160 | // Circular 161 | 162 | public static inline function circIn(t:FastFloat):FastFloat { 163 | return -(Math.sqrt(1 - t * t) - 1); 164 | } 165 | 166 | public static inline function circOut(t:FastFloat):FastFloat { 167 | return Math.sqrt(1 - (t - 1) * (t - 1)); 168 | } 169 | 170 | public static function circInOut(t:FastFloat):FastFloat { 171 | return t <= .5 ? (Math.sqrt(1 - t * t * 4) - 1) / -2 : (Math.sqrt(1 - (t * 2 - 2) * (t * 2 - 2)) + 1) / 2; 172 | } 173 | 174 | // Exponential 175 | 176 | public static inline function expoIn(t:FastFloat):FastFloat { 177 | return Math.pow(2, 10 * (t - 1)); 178 | } 179 | 180 | public static inline function expoOut(t:FastFloat):FastFloat { 181 | return -Math.pow(2, -10 * t) + 1; 182 | } 183 | 184 | public static function expoInOut(t:FastFloat):FastFloat { 185 | return t < .5 ? Math.pow(2, 10 * (t * 2 - 1)) / 2 : (-Math.pow(2, -10 * (t * 2 - 1)) + 2) / 2; 186 | } 187 | 188 | // Back 189 | 190 | public static inline function backIn(t:FastFloat):FastFloat { 191 | return t * t * (2.70158 * t - 1.70158); 192 | } 193 | 194 | public static inline function backOut(t:FastFloat):FastFloat { 195 | return 1 - (--t) * (t) * (-2.70158 * t - 1.70158); 196 | } 197 | 198 | public static function backInOut(t:FastFloat):FastFloat { 199 | t *= 2; 200 | if (t < 1) return t * t * (2.70158 * t - 1.70158) / 2; 201 | t--; 202 | return (1 - (--t) * (t) * (-2.70158 * t - 1.70158)) / 2 + .5; 203 | } 204 | 205 | // Elastic 206 | 207 | public static inline function elasticIn(t:FastFloat):FastFloat { 208 | return -(ELASTIC_AMPLITUDE * Math.pow(2, 10 * (t -= 1)) * Math.sin( (t - (ELASTIC_PERIOD / (2 * Math.PI) * Math.asin(1 / ELASTIC_AMPLITUDE))) * (2 * Math.PI) / ELASTIC_PERIOD)); 209 | } 210 | 211 | public static inline function elasticOut(t:FastFloat):FastFloat { 212 | return (ELASTIC_AMPLITUDE * Math.pow(2, -10 * t) * Math.sin((t - (ELASTIC_PERIOD / (2 * Math.PI) * Math.asin(1 / ELASTIC_AMPLITUDE))) * (2 * Math.PI) / ELASTIC_PERIOD) + 1); 213 | } 214 | 215 | public static function elasticInOut(t:FastFloat):FastFloat { 216 | if (t < 0.5) { 217 | return -0.5 * (Math.pow(2, 10 * (t -= 0.5)) * Math.sin((t - (ELASTIC_PERIOD / 4)) * (2 * Math.PI) / ELASTIC_PERIOD)); 218 | } 219 | return Math.pow(2, -10 * (t -= 0.5)) * Math.sin((t - (ELASTIC_PERIOD / 4)) * (2 * Math.PI) / ELASTIC_PERIOD) * 0.5 + 1; 220 | } 221 | 222 | } 223 | 224 | typedef EaseFunction = FastFloat->FastFloat; -------------------------------------------------------------------------------- /kala/debug/Console.hx: -------------------------------------------------------------------------------- 1 | package kala.debug; 2 | 3 | #if (debug || kala_debug) 4 | class Console { 5 | 6 | public function new() { 7 | 8 | } 9 | 10 | } 11 | #end -------------------------------------------------------------------------------- /kala/debug/Debug.hx: -------------------------------------------------------------------------------- 1 | package kala.debug; 2 | 3 | #if (debug || kala_debug) 4 | import kala.debug.Debugger; 5 | import kha.Canvas; 6 | import kha.Framebuffer; 7 | #end 8 | 9 | import kala.math.color.Color; 10 | 11 | @:allow(kala.Kala) 12 | class Debug { 13 | 14 | public static var collisionDebug:Bool = false; 15 | 16 | #if (debug || kala_debug) 17 | public static var debugger(default, never):Debugger = new Debugger(); 18 | 19 | private static var _layers(default, null):Array> = new Array>(); 20 | #end 21 | 22 | public static inline function log(value:Dynamic):Void { 23 | #if (debug || kala_debug) 24 | debugger.pushConsoleOutput(Std.string(value), Color.WHITE); 25 | #end 26 | } 27 | 28 | public static inline function echo(value:Dynamic):Void { 29 | #if (debug || kala_debug) 30 | debugger.pushConsoleOutput(Std.string(value), Color.WHITE); 31 | debugger.visible = true; 32 | #end 33 | } 34 | 35 | public static inline function error(message:String):Void { 36 | #if (debug || kala_debug) 37 | debugger.pushConsoleOutput("ERR: " + message, Color.RED); 38 | debugger.visible = true; 39 | #end 40 | } 41 | 42 | public static inline function print(value:Dynamic, ?textColor:Color = Color.WHITE, showDebugger:Bool = true):Void { 43 | #if (debug || kala_debug) 44 | debugger.pushConsoleOutput(Std.string(value), textColor); 45 | debugger.visible = showDebugger; 46 | #end 47 | } 48 | 49 | #if (debug || kala_debug) 50 | public static function addDrawLayer():Array { 51 | var layer = new Array(); 52 | _layers.push(layer); 53 | return layer; 54 | } 55 | 56 | static inline function draw(framebuffer:Framebuffer):Void { 57 | var previewCanvas:Canvas = null; 58 | 59 | var hasDrawCall = false; 60 | 61 | for (layer in _layers) { 62 | for (call in layer.copy()) { 63 | hasDrawCall = true; 64 | 65 | layer.remove(call); 66 | 67 | if (previewCanvas != call.canvas) { 68 | if (previewCanvas != null) previewCanvas.g2.end(); 69 | call.canvas.g2.begin(false); 70 | } 71 | 72 | call.exec(); 73 | 74 | previewCanvas = call.canvas; 75 | } 76 | } 77 | 78 | if (previewCanvas != null && previewCanvas != framebuffer) { 79 | previewCanvas.g2.end(); 80 | framebuffer.g2.begin(false); 81 | } 82 | 83 | if (debugger.visible) debugger.draw(framebuffer); 84 | } 85 | #end 86 | 87 | } 88 | 89 | #if (debug || kala_debug) 90 | class DebugDrawCall { 91 | 92 | public var canvas:Canvas; 93 | public var callback:Canvas->Void; 94 | 95 | public inline function new(canvas:Canvas, callback:Canvas->Void) { 96 | this.canvas = canvas; 97 | this.callback = callback; 98 | } 99 | 100 | @:extern 101 | public inline function exec():Void { 102 | callback(canvas); 103 | } 104 | 105 | } 106 | #end 107 | -------------------------------------------------------------------------------- /kala/debug/Tab.hx: -------------------------------------------------------------------------------- 1 | package kala.debug; 2 | 3 | #if (debug || kala_debug) 4 | import kha.FastFloat; 5 | import kha.Image; 6 | 7 | class Tab { 8 | 9 | public function new() { 10 | 11 | } 12 | 13 | public function draw(buffer:Image, hpos:FastFloat, vpos:FastFloat):Void { 14 | 15 | } 16 | 17 | } 18 | #end -------------------------------------------------------------------------------- /kala/graphics/Shader.hx: -------------------------------------------------------------------------------- 1 | package kala.graphics; 2 | 3 | import kala.behaviors.Behavior; 4 | import kala.objects.Object; 5 | import kha.graphics4.FragmentShader; 6 | import kha.graphics4.PipelineState; 7 | import kha.graphics4.VertexData; 8 | import kha.graphics4.VertexShader; 9 | import kha.graphics4.VertexStructure; 10 | import kha.Image; 11 | import kha.Shaders; 12 | 13 | /** 14 | * Used to set pipeline state for object rendering. 15 | * Can be added to object via object.addShader(). 16 | * Multiple shaders can be chained together on a same object. 17 | * For single shader, it will be faster to set canvas pipeline directly via onPreDraw of object. 18 | */ 19 | @:allow(kala.objects.Object) 20 | @:allow(kala.behaviors.Behavior) 21 | class Shader { 22 | 23 | public var pipeline:PipelineState; 24 | 25 | public var size:UInt; 26 | 27 | public function destroy():Void { 28 | pipeline = null; 29 | } 30 | 31 | // 32 | 33 | function createPipeline(?structure:VertexStructure, ?vertexShader:VertexShader, ?fragmentShader:FragmentShader):Void { 34 | pipeline = new PipelineState(); 35 | 36 | if (structure == null) { 37 | structure = new VertexStructure(); 38 | structure.add("vertexPosition", VertexData.Float3); 39 | structure.add("texPosition", VertexData.Float2); 40 | structure.add("vertexColor", VertexData.Float4); 41 | } 42 | 43 | pipeline.inputLayout = [structure]; 44 | 45 | pipeline.vertexShader = (vertexShader == null) ? Shaders.painter_image_vert : vertexShader; 46 | pipeline.fragmentShader = (fragmentShader == null) ? Shaders.painter_image_frag : fragmentShader; 47 | 48 | pipeline.compile(); 49 | } 50 | 51 | function update(texture:Image, buffer:Image):Void { 52 | 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /kala/input/ButtonInputHandle.hx: -------------------------------------------------------------------------------- 1 | package kala.input; 2 | 3 | import kala.EventHandle.CallbackHandle; 4 | import kha.FastFloat; 5 | 6 | class ButtonInputHandle { 7 | 8 | public var inputs:Array> = new Array>(); 9 | public var activeInputs:Array> = new Array>(); 10 | 11 | public var inputAny:ButtonInput; 12 | 13 | public var onStartPressing(default, null):CallbackHandleVoid>; 14 | public var onRelease(default, null):CallbackHandleVoid>; 15 | 16 | public function new(onStartPressing:CallbackHandleVoid>, onRelease:CallbackHandleVoid>):Void { 17 | this.onStartPressing = onStartPressing; 18 | this.onRelease = onRelease; 19 | } 20 | 21 | public function addButton(button:T):ButtonInput { 22 | var input = new ButtonInput(button, this); 23 | 24 | if (button == null) inputAny = input; 25 | else inputs.push(input); 26 | 27 | return input; 28 | } 29 | 30 | public function update(elapsed:FastFloat):Void { 31 | if (activeInputs.length == 0) { 32 | if (inputAny.duration > -1) { 33 | inputAny.justReleased = true; 34 | inputAny.duration = -1; 35 | } else { 36 | inputAny.justReleased = false; 37 | } 38 | 39 | return; 40 | } 41 | 42 | if (inputAny.duration == -1) inputAny.duration = 0; 43 | else inputAny.duration += elapsed; 44 | 45 | var i = activeInputs.length; 46 | var input:ButtonInput; 47 | while (i-- > 0) { 48 | input = activeInputs[i]; 49 | 50 | if (input.duration == -1) { 51 | if (input._state == 1) { 52 | input.duration = 0; 53 | for (callback in onStartPressing) callback.cbFunction(input.button); 54 | } else if (input.justReleased) { 55 | activeInputs.splice(i, 1); 56 | input.justReleased = false; 57 | } 58 | } else { 59 | if (input._state == 2) { 60 | input.duration = -1; 61 | input._state = 0; 62 | input.justReleased = true; 63 | for (callback in onRelease) callback.cbFunction(input.button); 64 | } else { 65 | input.duration += elapsed; 66 | } 67 | } 68 | } 69 | } 70 | 71 | public function checkAnyPressed(buttons:Array):Bool { 72 | var btns = buttons.copy(); 73 | var i:Int; 74 | for (input in activeInputs) { 75 | i = 0; 76 | for (button in btns) { 77 | if (input.button.equals(button)) { 78 | if (input.pressed) return true; 79 | if (btns.length == 1) return false; 80 | btns.splice(i, 1); 81 | continue; 82 | } 83 | 84 | i++; 85 | } 86 | } 87 | 88 | return false; 89 | } 90 | 91 | public function checkAnyJustPressed(buttons:Array):Bool { 92 | var btns = buttons.copy(); 93 | var i:Int; 94 | for (input in activeInputs) { 95 | i = 0; 96 | for (button in btns) { 97 | if (input.button.equals(button)) { 98 | if (input.justPressed) return true; 99 | if (btns.length == 1) return false; 100 | btns.splice(i, 1); 101 | continue; 102 | } 103 | 104 | i++; 105 | } 106 | } 107 | 108 | return false; 109 | } 110 | 111 | public function checkAnyJustReleased(buttons:Array):Bool { 112 | var btns = buttons.copy(); 113 | var i:Int; 114 | for (input in activeInputs) { 115 | i = 0; 116 | for (button in btns) { 117 | if (input.button.equals(button)) { 118 | if (input.justReleased) return true; 119 | if (btns.length == 1) return false; 120 | btns.splice(i, 1); 121 | continue; 122 | } 123 | 124 | i++; 125 | } 126 | } 127 | 128 | return false; 129 | } 130 | 131 | public function checkAllPressed(buttons:Array):Bool { 132 | var btns = buttons.copy(); 133 | var i:Int; 134 | 135 | for (input in activeInputs) { 136 | i = 0; 137 | for (button in btns) { 138 | if (input.button.equals(button)) { 139 | if (!input.pressed) return false; 140 | if (btns.length == 1) return true; 141 | btns.splice(i, 1); 142 | continue; 143 | } 144 | 145 | i++; 146 | } 147 | } 148 | 149 | return false; 150 | } 151 | 152 | public function checkAllJustPressed(buttons:Array):Bool { 153 | var btns = buttons.copy(); 154 | var i:Int; 155 | for (input in activeInputs) { 156 | i = 0; 157 | for (button in btns) { 158 | if (input.button.equals(button)) { 159 | if (!input.justPressed) return false; 160 | if (btns.length == 1) return true; 161 | btns.splice(i, 1); 162 | continue; 163 | } 164 | 165 | i++; 166 | } 167 | } 168 | 169 | return false; 170 | } 171 | 172 | public function checkAllJustReleased(buttons:Array):Bool { 173 | var btns = buttons.copy(); 174 | var i:Int; 175 | for (input in activeInputs) { 176 | i = 0; 177 | for (button in btns) { 178 | if (input.button.equals(button)) { 179 | if (!input.justReleased) return false; 180 | if (btns.length == 1) return true; 181 | btns.splice(i, 1); 182 | continue; 183 | } 184 | 185 | i++; 186 | } 187 | } 188 | 189 | return false; 190 | } 191 | 192 | } 193 | 194 | @:allow(kala.input.ButtonInputHandle) 195 | class ButtonInput { 196 | 197 | public var button(default, null):T; 198 | 199 | /** 200 | * The time this button has been pressed. 201 | * In seconds if Kala.deltaTiming is set to true otherwise in frames. 202 | * 0 means the button was just pressed. 203 | * -1 means the button is not pressed. 204 | */ 205 | public var duration(default, null):FastFloat = -1; 206 | 207 | public var pressed(get, never):Bool; 208 | public var justPressed(get, never):Bool; 209 | public var justReleased(default, null):Bool; 210 | 211 | private var _state:Int = 0; // 1 - waiting to be registered, 2 - waiting to be released 212 | 213 | private var _handle:ButtonInputHandle; 214 | 215 | public function new(button:T, handle:ButtonInputHandle) { 216 | this.button = button; 217 | _handle = handle; 218 | } 219 | 220 | inline function waitForRegistration():Void { 221 | _handle.activeInputs.push(this); 222 | _state = 1; 223 | } 224 | 225 | inline function waitForReleasing():Void { 226 | _state = 2; 227 | } 228 | 229 | inline function get_pressed():Bool { 230 | return duration > -1; 231 | } 232 | 233 | inline function get_justPressed():Bool { 234 | return duration == 0; 235 | } 236 | 237 | } -------------------------------------------------------------------------------- /kala/input/Mouse.hx: -------------------------------------------------------------------------------- 1 | package kala.input; 2 | import kala.math.Vec2; 3 | import kala.objects.group.View; 4 | import kala.objects.Object; 5 | 6 | #if (debug || kala_debug || kala_mouse) 7 | 8 | import kala.EventHandle.CallbackHandle; 9 | import kala.input.ButtonInputHandle; 10 | import kala.math.Rect; 11 | import kha.FastFloat; 12 | 13 | @:allow(kala.Kala) 14 | @:access(kala.CallbackHandle) 15 | @:access(kala.input.ButtonInput) 16 | class Mouse { 17 | 18 | public static var onStartPressing(default, never):CallbackHandleVoid> = new CallbackHandleVoid>(); 19 | public static var onRelease(default, never):CallbackHandleVoid> = new CallbackHandleVoid>(); 20 | 21 | // 22 | 23 | public static var x(default, null):Int = 0; 24 | public static var y(default, null):Int = 0; 25 | 26 | public static var realX(default, null):Int = 0; 27 | public static var realY(default, null):Int = 0; 28 | 29 | public static var wheel(get, null):Int; 30 | 31 | // 32 | 33 | public static var ANY(default, null):ButtonInput; 34 | public static var LEFT(default, null):ButtonInput; 35 | public static var RIGHT(default, null):ButtonInput; 36 | public static var MIDDLE(default, null):ButtonInput; 37 | 38 | // 39 | 40 | private static var _wheel:Int; 41 | private static var _wheelRegistered:Bool = false; 42 | 43 | private static var _handle:ButtonInputHandle; 44 | 45 | // 46 | 47 | public static inline function checkAnyPressed(buttons:Array):Bool { 48 | return _handle.checkAnyPressed(buttons); 49 | } 50 | 51 | public static inline function checkAnyJustPressed(buttons:Array):Bool { 52 | return _handle.checkAnyJustPressed(buttons); 53 | } 54 | 55 | public static inline function checkAnyJustReleased(buttons:Array):Bool { 56 | return _handle.checkAnyJustReleased(buttons); 57 | } 58 | 59 | public static inline function checkAllPressed(buttons:Array):Bool { 60 | return _handle.checkAllPressed(buttons); 61 | } 62 | 63 | public static inline function checkAllJustPressed(buttons:Array):Bool { 64 | return _handle.checkAllJustPressed(buttons); 65 | } 66 | 67 | public static inline function checkAllJustReleased(buttons:Array):Bool { 68 | return _handle.checkAllJustReleased(buttons); 69 | } 70 | 71 | // 72 | 73 | public static inline function isHovering(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):Bool { 74 | return ( 75 | Mouse.x >= x && Mouse.x <= x + width && 76 | Mouse.y >= y && Mouse.y <= y + height 77 | ); 78 | } 79 | 80 | public static inline function isHoveringRect(rect:Rect):Bool { 81 | return ( 82 | Mouse.x >= rect.x && Mouse.x <= rect.x + rect.width && 83 | Mouse.y >= rect.y && Mouse.y <= rect.y + rect.height 84 | ); 85 | } 86 | 87 | public static inline function isHoveringObject(object:Object):Bool { 88 | return ( 89 | Mouse.x >= object.x && Mouse.x <= object.x + object.width && 90 | Mouse.y >= object.y && Mouse.y <= object.y + object.height 91 | ); 92 | } 93 | 94 | public static inline function didLeftClickOn(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):Bool { 95 | return LEFT.justPressed && isHovering(x, y, width, height); 96 | } 97 | 98 | public static inline function didLeftClickRect(rect:Rect):Bool { 99 | return LEFT.justPressed && isHoveringRect(rect); 100 | } 101 | 102 | public static inline function didLeftClickObject(object:Object):Bool { 103 | return LEFT.justPressed && isHoveringObject(object); 104 | } 105 | 106 | public static inline function didRightClickOn(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):Bool { 107 | return RIGHT.justPressed && isHovering(x, y, width, height); 108 | } 109 | 110 | public static inline function didRightClickRect(rect:Rect):Bool { 111 | return RIGHT.justPressed && isHoveringRect(rect); 112 | } 113 | 114 | public static inline function didRightClickObject(object:Object):Bool { 115 | return RIGHT.justPressed && isHoveringObject(object); 116 | } 117 | 118 | /** 119 | * Project the cursor real position from the input view to its viewport. 120 | * Only works when the view is visible. 121 | */ 122 | public static inline function project(view:View):Vec2 { 123 | return view.project(realX, realY); 124 | } 125 | 126 | // 127 | 128 | /* 129 | static inline function buttonIndexToButton(index:Int):MouseButton { 130 | return MouseButton.createByIndex(index + 1); 131 | } 132 | */ 133 | 134 | static function init():Void { 135 | kha.input.Mouse.get().notify(mouseDownListener, mouseUpListener, mouseMoveListener, onWheel); 136 | 137 | _handle = new ButtonInputHandle(onStartPressing, onRelease); 138 | 139 | ANY = _handle.addButton(null); 140 | 141 | LEFT = _handle.addButton(MouseButton.LEFT); 142 | RIGHT = _handle.addButton(MouseButton.RIGHT); 143 | MIDDLE = _handle.addButton(MouseButton.MIDDLE); 144 | } 145 | 146 | static inline function update(elapsed:FastFloat):Void { 147 | _handle.update(elapsed); 148 | 149 | if (_wheel != 0) { 150 | if (_wheelRegistered) _wheel = 0; 151 | else _wheelRegistered = true; 152 | } 153 | } 154 | 155 | static function mouseDownListener(button:Int, x:Int, y:Int):Void { 156 | _handle.inputs[button].waitForRegistration(); 157 | } 158 | 159 | static function mouseUpListener(button:Int, x:Int, y:Int):Void { 160 | _handle.inputs[button].waitForReleasing(); 161 | } 162 | 163 | static function mouseMoveListener(x:Int, y:Int, _:Int, _:Int):Void { 164 | if (Kala.defaultView == null) { 165 | Mouse.x = realX = x; 166 | Mouse.y = realY = y; 167 | } else { 168 | realX = x; 169 | realY = y; 170 | 171 | var p = Kala.defaultView.project(x, y); 172 | Mouse.x = Std.int(p.x); 173 | Mouse.y = Std.int(p.y); 174 | } 175 | } 176 | 177 | static function onWheel(amount:Int):Void { 178 | _wheel = amount; 179 | _wheelRegistered = false; 180 | } 181 | 182 | static function get_wheel():Int { 183 | return _wheelRegistered ? _wheel : 0; 184 | } 185 | 186 | } 187 | 188 | enum MouseButton { 189 | 190 | LEFT; 191 | RIGHT; 192 | MIDDLE; 193 | 194 | } 195 | 196 | #end -------------------------------------------------------------------------------- /kala/input/Touch.hx: -------------------------------------------------------------------------------- 1 | package kala.input; 2 | 3 | #if kala_touch 4 | 5 | import kala.EventHandle.CallbackHandle; 6 | import kala.math.Rect.RectI; 7 | import kala.math.Vec2; 8 | import kala.objects.group.View; 9 | import kha.FastFloat; 10 | import kha.input.Surface; 11 | 12 | @:allow(kala.Kala) 13 | @:allow(kala.input.TouchHandle) 14 | @:access(kala.input.TouchHandle) 15 | class Touch { 16 | 17 | public static var onStart(default, never):CallbackHandleVoid> = new CallbackHandleVoid>(); 18 | public static var onEnd(default, never):CallbackHandleVoid> = new CallbackHandleVoid>(); 19 | public static var onMove(default, never):CallbackHandleVoid> = new CallbackHandleVoid>(); 20 | 21 | public static var touches(default, never):TouchHandle = new TouchHandle(); 22 | public static var count(default, null):Int = 0; 23 | 24 | static function init():Void { 25 | var surface = Surface.get(); 26 | if (surface != null) surface.notify(touchStartListener, touchEndListener, touchMoveListener); 27 | } 28 | 29 | static function touchStartListener(id:Int, x:Int, y:Int):Void { 30 | touches.count++; 31 | 32 | if (Kala.defaultView == null) { 33 | touches._capturedTouches.push(new Touch(id, x, y, x, y)); 34 | } else { 35 | var p = Kala.defaultView.project(x, y); 36 | touches._capturedTouches.push(new Touch(id, Std.int(p.x), Std.int(p.y), x, y)); 37 | } 38 | } 39 | 40 | static function touchEndListener(id:Int, x:Int, y:Int):Void { 41 | touches.count--; 42 | touches.findTouch(id)._ending = true; 43 | } 44 | 45 | static function touchMoveListener(id:Int, x:Int, y:Int):Void { 46 | var touch = touches.findTouch(id); 47 | touch.setPos(x, y); 48 | for (callback in onMove) callback.cbFunction(touch); 49 | } 50 | 51 | static inline function update(elapsed:FastFloat):Void { 52 | touches.update(elapsed); 53 | } 54 | 55 | // 56 | 57 | public var id(default, null):Int; 58 | 59 | public var x(default, null):Int; 60 | public var y(default, null):Int; 61 | 62 | public var realX(default, null):Int; 63 | public var realY(default, null):Int; 64 | 65 | public var duration(default, null):FastFloat; 66 | 67 | public var justStarted(get, never):Bool; 68 | public var justEnded(default, null):Bool; 69 | 70 | private var _ending:Bool; 71 | 72 | public inline function new(id:Int, x:Int, y:Int, realX:Int, realY:Int) { 73 | this.id = id; 74 | 75 | this.x = x; 76 | this.y = y; 77 | this.realX = realX; 78 | this.realY = realY; 79 | 80 | duration = 0; 81 | _ending = false; 82 | } 83 | 84 | /** 85 | * Project the touch real position from the input view to its viewport. 86 | * Only works when the view is visible. 87 | */ 88 | public inline function project(view:View):Vec2 { 89 | return view.project(realX, realY); 90 | } 91 | 92 | @:extern 93 | inline function setPos(x:Int, y:Int):Void { 94 | if (Kala.defaultView == null) { 95 | this.x = realX = x; 96 | this.y = realY = y; 97 | } else { 98 | realX = x; 99 | realY = y; 100 | 101 | var p = Kala.defaultView.project(x, y); 102 | x = Std.int(p.x); 103 | y = Std.int(p.y); 104 | } 105 | } 106 | 107 | inline function get_justStarted():Bool { 108 | return duration == 0; 109 | } 110 | 111 | } 112 | 113 | class TouchHandle { 114 | 115 | public var count(default, null):Int = 0; 116 | 117 | private var _registeredTouches:Array = new Array(); 118 | private var _capturedTouches:Array = new Array(); 119 | 120 | public function new() { 121 | 122 | } 123 | 124 | public inline function get(index:Int):Touch { 125 | return _registeredTouches[index]; 126 | } 127 | 128 | public function findTouch(id:Int):Touch { 129 | for (touch in _registeredTouches) { 130 | if (touch.id == id) return touch; 131 | } 132 | 133 | return null; 134 | } 135 | 136 | /** 137 | * Return: 138 | * 0 if having no touch. 139 | * 1 if having touches only on the left or upper side. 140 | * 2 if having touches only on the right or lower side. 141 | * 3 if having touches both sides. 142 | */ 143 | public function getSide(?vertical:Bool = false, ?middlePoint:FastFloat):Int { 144 | if (middlePoint == null) middlePoint = vertical ? Kala.height / 2 : Kala.width / 2; 145 | 146 | var leftUpperTouched = false; 147 | var rightLowerTouched = false; 148 | 149 | if (vertical) { 150 | for (touch in _registeredTouches) { 151 | if (touch.y < middlePoint) leftUpperTouched = true; 152 | else rightLowerTouched = true; 153 | } 154 | } else { 155 | for (touch in _registeredTouches) { 156 | if (touch.x < middlePoint) leftUpperTouched = true; 157 | else rightLowerTouched = true; 158 | } 159 | } 160 | 161 | if (leftUpperTouched && rightLowerTouched) return 3; 162 | if (rightLowerTouched) return 2; 163 | if (leftUpperTouched) return 1; 164 | return 0; 165 | } 166 | 167 | public function iterator():Iterator { 168 | return _registeredTouches.iterator(); 169 | } 170 | 171 | function update(elapsed:FastFloat):Void { 172 | var i = _registeredTouches.length; 173 | var touch:Touch; 174 | while (i-- > 0) { 175 | touch = _registeredTouches[i]; 176 | touch.duration += elapsed; 177 | 178 | if (touch._ending) { 179 | if (touch.justEnded) _registeredTouches.splice(i, 1); 180 | else touch.justEnded = true; 181 | 182 | for (callback in Touch.onEnd) callback.cbFunction(touch); 183 | 184 | if (_registeredTouches.length == 0 && count > 0) { 185 | while (_registeredTouches.length > 0) _registeredTouches.pop(); 186 | } 187 | } 188 | } 189 | 190 | var i = 0; 191 | var touch:Touch; 192 | while (_capturedTouches.length > 0) { 193 | touch = _capturedTouches.pop(); 194 | _registeredTouches.push(touch); 195 | for (callback in Touch.onStart) callback.cbFunction(touch); 196 | i++; 197 | } 198 | } 199 | 200 | } 201 | 202 | #end -------------------------------------------------------------------------------- /kala/lang/Builder.hx: -------------------------------------------------------------------------------- 1 | package kala.lang; 2 | 3 | class Builder { 4 | 5 | } -------------------------------------------------------------------------------- /kala/lang/Lang.hx: -------------------------------------------------------------------------------- 1 | package kala.lang; 2 | 3 | class Lang { 4 | 5 | } -------------------------------------------------------------------------------- /kala/math/Mathf.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | class Mathf { 6 | 7 | /** 8 | * Degrees to radians conversion constant. 9 | */ 10 | public static var CONST_RAD(default, never):FastFloat = Math.PI / 180; 11 | /** 12 | * Radians to degrees conversion constant. 13 | */ 14 | public static var CONST_DEG(default, never):FastFloat = 180 / Math.PI; 15 | 16 | // 17 | 18 | /** 19 | * Converts radians to degrees. 20 | */ 21 | @:extern 22 | public static inline function rad(deg:FastFloat):FastFloat { 23 | return deg * CONST_RAD; 24 | } 25 | 26 | /** 27 | * Converts degrees to radians. 28 | */ 29 | @:extern 30 | public static inline function deg(rad:FastFloat):FastFloat { 31 | return rad * CONST_DEG; 32 | } 33 | 34 | /** 35 | * Wraps a value between -180 and 180 if the 'positive' parameter set to false 36 | * otherwise wrap between 0 and 360. 37 | */ 38 | @:extern 39 | public static inline function wrapDeg(angle:FastFloat, positive:Bool = false):FastFloat { 40 | angle %= 360; 41 | if (positive) return (angle + 360) % 360; 42 | return angle; 43 | } 44 | 45 | /** 46 | * Gets angle between two points. 47 | */ 48 | @:extern 49 | public static inline function angle( 50 | x1:FastFloat, y1:FastFloat, 51 | x2:FastFloat, y2:FastFloat, 52 | asDeg:Bool = true 53 | ):FastFloat { 54 | return Math.atan2(y2 - y1, x2 - x1) * (asDeg ? Mathf.CONST_DEG : 1); 55 | } 56 | 57 | /** 58 | * Gets distance between two points. 59 | */ 60 | @:extern 61 | public static inline function distance( 62 | x1:FastFloat, y1:FastFloat, 63 | x2:FastFloat, y2:FastFloat 64 | ):FastFloat { 65 | return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); 66 | } 67 | 68 | /** 69 | * Clamps a value between a minimum and a maximum value. 70 | */ 71 | @:extern 72 | public static inline function clamp(value:FastFloat, min:FastFloat, max:FastFloat):FastFloat { 73 | return Math.max(min, Math.min(max, value)); 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /kala/math/Matrix.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | import kha.math.FastMatrix3; 5 | import kha.math.Matrix3; 6 | 7 | @:forward 8 | abstract Matrix(FastMatrix3) from FastMatrix3 to FastMatrix3 { 9 | 10 | @:extern @:from 11 | public static inline function fromMatrix3(matrix:Matrix3):Matrix { 12 | return new FastMatrix3( 13 | matrix._00, matrix._10, matrix._20, 14 | matrix._01, matrix._11, matrix._21, 15 | matrix._02, matrix._12, matrix._22 16 | ); 17 | } 18 | 19 | @:extern 20 | public static inline function getTransformation( 21 | position:Position, scale:Vec2T, rotation:Rotation 22 | ):Matrix { 23 | var x = position.realX; 24 | var y = position.realY; 25 | 26 | // Positing 27 | var matrix = Matrix.translation(x, y); 28 | 29 | // Scaling 30 | var ox = x + scale.ox; 31 | var oy = y + scale.oy; 32 | 33 | matrix = Matrix.translation(ox, oy) 34 | .multmat(Matrix.scale(scale.x, scale.y)) 35 | .multmat(Matrix.translation( -ox, -oy)) 36 | .multmat(matrix); 37 | 38 | /* 39 | // Skewing 40 | ox = x + skew.ox; 41 | oy = y + skew.oy; 42 | 43 | matrix = Matrix.translation(ox, oy) 44 | .multmat(new Matrix(1, Math.tan(skew.x * Mathf.CONST_RAD), 0, Math.tan(skew.y * Mathf.CONST_RAD), 1, 0)) 45 | .multmat(Matrix.translation( -ox, -oy)) 46 | .multmat(matrix); 47 | */ 48 | 49 | // Rotating 50 | ox = x + rotation.px; 51 | oy = y + rotation.py; 52 | 53 | matrix = Matrix.translation(ox, oy) 54 | .multmat(Matrix.rotation(rotation.rad)) 55 | .multmat(Matrix.translation( -ox, -oy)) 56 | .multmat(matrix); 57 | 58 | return matrix; 59 | } 60 | 61 | @:extern 62 | public static inline function flip( 63 | matrix:FastMatrix3, flipX:Bool, flipY:Bool, ox:FastFloat, oy:FastFloat 64 | ):Matrix { 65 | return Matrix.translation(ox, oy) 66 | .multmat(Matrix.scale(flipX ? -1 : 1, flipY ? -1 : 1)) 67 | .multmat(Matrix.translation( -ox, -oy)) 68 | .multmat(matrix); 69 | } 70 | 71 | 72 | @:extern 73 | public static inline function translation(x:FastFloat, y:FastFloat):Matrix { 74 | return new Matrix( 75 | 1, 0, x, 76 | 0, 1, y 77 | ); 78 | } 79 | 80 | @:extern 81 | public static inline function identity():Matrix { 82 | return new Matrix( 83 | 1, 0, 0, 84 | 0, 1, 0 85 | ); 86 | } 87 | 88 | @:extern 89 | public static inline function scale(x:FastFloat, y:FastFloat):Matrix { 90 | return new Matrix( 91 | x, 0, 0, 92 | 0, y, 0 93 | ); 94 | } 95 | 96 | @:extern 97 | public static inline function rotation(alpha:FastFloat):Matrix { 98 | return new Matrix( 99 | Math.cos(alpha), -Math.sin(alpha), 0, 100 | Math.sin(alpha), Math.cos(alpha), 0 101 | ); 102 | } 103 | 104 | // 105 | 106 | public var a(get, set):FastFloat; 107 | public var b(get, set):FastFloat; 108 | public var c(get, set):FastFloat; 109 | public var d(get, set):FastFloat; 110 | public var tx(get, set):FastFloat; 111 | public var ty(get, set):FastFloat; 112 | 113 | public inline function new(a:Float = 1, c:Float = 0, tx:Float = 0, b:Float = 0, d:Float = 1, ty:Float = 0) { 114 | this = new FastMatrix3(a, c, tx, b, d, ty, 0, 0, 1); 115 | } 116 | 117 | @:extern @:to 118 | public inline function toMatrix3():Matrix3 { 119 | return new Matrix3( 120 | this._00, this._10, this._20, 121 | this._01, this._11, this._21, 122 | this._02, this._12, this._22 123 | ); 124 | } 125 | 126 | @:extern 127 | public inline function clone():Matrix { 128 | return new FastMatrix3( 129 | this._00, this._10, this._20, 130 | this._01, this._11, this._21, 131 | this._02, this._12, this._22 132 | ); 133 | } 134 | 135 | @:extern 136 | public inline function compare(matrix:Matrix):Bool { 137 | return !( 138 | this._00 != matrix._00 || this._10 != matrix._10 || this._20 != matrix._20 || 139 | this._01 != matrix._01 || this._11 != matrix._11 || this._21 != matrix._21 || 140 | this._02 != matrix._02 || this._12 != matrix._12 || this._22 != matrix._22 141 | ); 142 | } 143 | 144 | @:extern 145 | inline function get_a():FastFloat { 146 | return this._00; 147 | } 148 | 149 | @:extern 150 | inline function set_a(value:FastFloat):FastFloat { 151 | return this._00 = value; 152 | } 153 | 154 | @:extern 155 | inline function get_b():FastFloat { 156 | return this._01; 157 | } 158 | 159 | @:extern 160 | inline function set_b(value:FastFloat):FastFloat { 161 | return this._01 = value; 162 | } 163 | 164 | @:extern 165 | inline function get_c():FastFloat { 166 | return this._10; 167 | } 168 | 169 | @:extern 170 | inline function set_c(value:FastFloat):FastFloat { 171 | return this._10 = value; 172 | } 173 | 174 | @:extern 175 | inline function get_d():FastFloat { 176 | return this._11; 177 | } 178 | 179 | @:extern 180 | inline function set_d(value:FastFloat):FastFloat { 181 | return this._11 = value; 182 | } 183 | 184 | @:extern 185 | inline function get_tx():FastFloat { 186 | return this._20; 187 | } 188 | 189 | @:extern 190 | inline function set_tx(value:FastFloat):FastFloat { 191 | return this._20 = value; 192 | } 193 | 194 | @:extern 195 | inline function get_ty():FastFloat { 196 | return this._21; 197 | } 198 | 199 | @:extern 200 | inline function set_ty(value:FastFloat):FastFloat { 201 | return this._21 = value; 202 | } 203 | 204 | } -------------------------------------------------------------------------------- /kala/math/Position.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | @:forward 6 | abstract Position(Vec2T) from Vec2T to Vec2T { 7 | 8 | public var realX(get, never):FastFloat; 9 | public var realY(get, never):FastFloat; 10 | 11 | public inline function new(x:FastFloat = 0, y:FastFloat = 0, ox:FastFloat = 0, oy:FastFloat = 0) { 12 | this = new Vec2T(x, y, ox, oy); 13 | } 14 | 15 | public inline function getDistance(pos:Position):FastFloat { 16 | return Math.sqrt((this.x - pos.x) * (this.x - pos.x) + (this.y - pos.y) * (this.y - pos.y)); 17 | } 18 | 19 | public inline function getAngle(pos:Position, asDeg:Bool = true):FastFloat { 20 | return Math.atan2(pos.y - this.y, pos.x - this.x) * (asDeg ? Mathf.CONST_DEG : 1); 21 | } 22 | 23 | inline function get_realX():FastFloat { 24 | return this.x - this.ox; 25 | } 26 | 27 | inline function get_realY():FastFloat { 28 | return this.y - this.oy; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /kala/math/Random.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | abstract Random(kha.math.Random) from kha.math.Random to kha.math.Random { 6 | 7 | public static var instance:Random = new Random(6172039); 8 | 9 | public static inline function int(min:Int, max:Int):Int { 10 | return instance.getInt(min, max); 11 | } 12 | 13 | public static inline function float(min:Float, max:Float):Float { 14 | return instance.getFloat(min, max); 15 | } 16 | 17 | public static inline function fast(min:FastFloat, max:FastFloat):FastFloat { 18 | return instance.getFast(min, max); 19 | } 20 | 21 | public static inline function bool(chance:Float = 50):Bool { 22 | return int(0, 100) < chance; 23 | } 24 | 25 | /** 26 | * Randomly return 1 or -1 based on the input chance. 27 | */ 28 | public static inline function roll(chance:Float = 50):Int { 29 | if (int(0, 1) == 0) return -1; 30 | return 1; 31 | } 32 | 33 | // 34 | 35 | 36 | public inline function new(seed:Int) { 37 | this = new kha.math.Random(seed); 38 | } 39 | 40 | public inline function getInt(min:Int, max:Int):Int { 41 | return this.GetIn(min, max); 42 | } 43 | 44 | public inline function getFloat(min:Float, max:Float):Float { 45 | return this.GetFloatIn(min, max); 46 | } 47 | 48 | public inline function getFast(min:FastFloat, max:FastFloat):FastFloat { 49 | return this.GetFloatIn(min, max); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /kala/math/Rect.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | class Rect { 6 | 7 | public var x:FastFloat; 8 | public var y:FastFloat; 9 | public var width:FastFloat; 10 | public var height:FastFloat; 11 | 12 | public inline function new(x:FastFloat = 0, y:FastFloat = 0, width:FastFloat = 0, height:FastFloat = 0) { 13 | this.x = x; 14 | this.y = y; 15 | this.width = width; 16 | this.height = height; 17 | } 18 | 19 | @:extern 20 | public inline function set(x:FastFloat = 0, y:FastFloat = 0, width:FastFloat = 0, height:FastFloat = 0):Void { 21 | this.x = x; 22 | this.y = y; 23 | this.width = width; 24 | this.height = height; 25 | } 26 | 27 | @:extern 28 | public inline function copy(rect:Rect):Void { 29 | x = rect.x; 30 | y = rect.y; 31 | width = rect.width; 32 | height = rect.height; 33 | } 34 | 35 | @:extern 36 | public inline function clone():Rect { 37 | return new Rect(x, y, width, height); 38 | } 39 | 40 | /** 41 | * Get the intersection arena of this rectangle with the input rectangle. 42 | */ 43 | @:extern 44 | public inline function getIntersection(rect:Rect):Rect { 45 | var x2 = x + width; 46 | var x4 = rect.x + rect.width; 47 | var y1 = y - height; 48 | var y3 = rect.y - rect.height; 49 | 50 | var leftX = Math.max(x, rect.x); 51 | var rightX = Math.min(x2, x4); 52 | 53 | if (rightX <= leftX) return null; 54 | else { 55 | var topY = Math.max(y1, y3); 56 | var bottomY = Math.min(y, rect.y); 57 | 58 | if (bottomY <= topY) return null; 59 | else return new Rect(leftX, bottomY, rightX - leftX, bottomY - topY); 60 | } 61 | } 62 | 63 | @:extern 64 | public inline function toString():String { 65 | return "Rect(x: " + x + ", y: " + y + ", w: " + width + ", h: " + height + ")"; 66 | } 67 | 68 | } 69 | 70 | class RectI { 71 | 72 | public var x:Int; 73 | public var y:Int; 74 | public var width:Int; 75 | public var height:Int; 76 | 77 | public inline function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0) { 78 | this.x = x; 79 | this.y = y; 80 | this.width = width; 81 | this.height = height; 82 | } 83 | 84 | @:extern 85 | public inline function set(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0):Void { 86 | this.x = x; 87 | this.y = y; 88 | this.width = width; 89 | this.height = height; 90 | } 91 | 92 | @:extern 93 | public inline function copy(rect:RectI):Void { 94 | x = rect.x; 95 | y = rect.y; 96 | width = rect.width; 97 | height = rect.height; 98 | } 99 | 100 | @:extern 101 | public inline function clone():RectI { 102 | return new RectI(x, y, width, height); 103 | } 104 | 105 | /** 106 | * Get the intersection arena of this rectangle with the input rectangle. 107 | */ 108 | @:extern 109 | public inline function getIntersection(rect:Rect):RectI { 110 | var x2 = x + width; 111 | var x4 = rect.x + rect.width; 112 | var y1 = y - height; 113 | var y3 = rect.y - rect.height; 114 | 115 | var leftX = Std.int(Math.max(x, rect.x)); 116 | var rightX = Std.int(Math.min(x2, x4)); 117 | 118 | if (rightX <= leftX) return null; 119 | else { 120 | var topY = Std.int(Math.max(y1, y3)); 121 | var bottomY = Std.int(Math.min(y, rect.y)); 122 | 123 | if (bottomY <= topY) return null; 124 | else return new RectI(leftX, bottomY, rightX - leftX, bottomY - topY); 125 | } 126 | } 127 | 128 | @:extern 129 | public inline function toString():String { 130 | return "RectI(x: " + x + ", y: " + y + ", w: " + width + ", h: " + height + ")"; 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /kala/math/Rotation.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | class Rotation { 6 | 7 | public var px:FastFloat; 8 | public var py:FastFloat; 9 | public var angle:FastFloat; 10 | 11 | public var asDeg:Bool; 12 | 13 | public var deg(get, set):FastFloat; 14 | public var rad(get, set):FastFloat; 15 | 16 | public inline function new(angle:FastFloat = 0, pivotX:FastFloat = 0, pivotY:FastFloat = 0, asDeg:Bool = true) { 17 | this.angle = angle; 18 | this.px = pivotX; 19 | this.py = pivotY; 20 | this.asDeg = asDeg; 21 | } 22 | 23 | @:extern 24 | public inline function set(angle:FastFloat = 0, pivotX:FastFloat = 0, pivotY:FastFloat = 0, asDeg:Bool = true):Rotation { 25 | this.angle = angle; 26 | this.px = pivotX; 27 | this.py = pivotY; 28 | this.asDeg = asDeg; 29 | 30 | return this; 31 | } 32 | 33 | @:extern 34 | public inline function copy(rotation:Rotation):Void { 35 | px = rotation.px; 36 | py = rotation.py; 37 | angle = rotation.angle; 38 | asDeg = rotation.asDeg; 39 | } 40 | 41 | @:extern 42 | public inline function clone():Rotation { 43 | return new Rotation(angle, px, py, asDeg); 44 | } 45 | 46 | @:extern 47 | public inline function setPivot(pivotX:FastFloat = 0, pivotY:FastFloat = 0):Rotation { 48 | this.px = pivotX; 49 | this.py = pivotY; 50 | 51 | return this; 52 | } 53 | 54 | /** 55 | * If asDeg is true, convert the angle value from radians to degrees 56 | * otherwise convert it from degrees to radians. 57 | */ 58 | @:extern 59 | public inline function convert():Rotation { 60 | if (asDeg) angle = Mathf.rad(angle); 61 | else angle = Mathf.deg(angle); 62 | 63 | asDeg = !asDeg; 64 | 65 | return this; 66 | } 67 | 68 | @:extern 69 | public inline function movePivot(px:FastFloat, py:FastFloat):Rotation { 70 | this.px += px; 71 | this.py += py; 72 | 73 | return this; 74 | } 75 | 76 | function get_deg():FastFloat { 77 | if (asDeg) return angle; 78 | return Mathf.deg(angle); 79 | } 80 | 81 | function set_deg(value:FastFloat):FastFloat { 82 | asDeg = true; 83 | return angle = value; 84 | } 85 | 86 | function get_rad():FastFloat { 87 | if (asDeg) return Mathf.rad(angle); 88 | return angle; 89 | } 90 | 91 | function set_rad(value:FastFloat):FastFloat { 92 | asDeg = false; 93 | return angle = value; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /kala/math/Vec2.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | import kha.math.FastVector2; 5 | import kha.math.Vector2; 6 | 7 | abstract Vec2(FastVector2) from FastVector2 to FastVector2 { 8 | 9 | @:extern @:from 10 | public static inline function fromVector2(vec:Vector2):Vec2 { 11 | return new Vec2(vec.x, vec.y); 12 | } 13 | 14 | @:extern 15 | public static inline function toVector2Array(vectors:Array):Array { 16 | return [for (vec in vectors) new Vector2(vec.x, vec.y)]; 17 | } 18 | 19 | // 20 | 21 | public var x(get, set):FastFloat; 22 | public var y(get, set):FastFloat; 23 | public var length(get, set):FastFloat; 24 | 25 | public inline function new(x:FastFloat = 0, y:FastFloat = 0): Void { 26 | this = new FastVector2(x, y); 27 | } 28 | 29 | @:extern @:to 30 | public inline function toVector2():Vector2 { 31 | return new Vector2(x, y); 32 | } 33 | 34 | @:extern 35 | public inline function set(x:FastFloat = 0, y:FastFloat = 0):Vec2 { 36 | this.x = x; 37 | this.y = y; 38 | 39 | return this; 40 | } 41 | 42 | @:extern 43 | public inline function clone():Vec2 { 44 | return new Vec2(x, y); 45 | } 46 | 47 | @:extern 48 | public inline function copy(vec:Vec2):Vec2 { 49 | x = vec.x; 50 | y = vec.y; 51 | 52 | return this; 53 | } 54 | 55 | @:extern 56 | public inline function add(vec:Vec2):Vec2 { 57 | return new Vec2(x + vec.x, y + vec.y); 58 | } 59 | 60 | @:extern 61 | public inline function sub(vec:Vec2):Vec2 { 62 | return new Vec2(x - vec.x, y - vec.y); 63 | } 64 | 65 | @:extern 66 | public inline function mult(value:FastFloat):Vec2 { 67 | return new Vec2(x * value, y * value); 68 | } 69 | 70 | @:extern 71 | public inline function div(value:FastFloat):Vec2 { 72 | return mult(1 / value); 73 | } 74 | 75 | @:extern 76 | public inline function addBy(vec:Vec2):Vec2 { 77 | x += vec.x; 78 | y += vec.y; 79 | 80 | return this; 81 | } 82 | 83 | @:extern 84 | public inline function move(x:FastFloat, y:FastFloat):Void { 85 | this.x += x; 86 | this.y += y; 87 | } 88 | 89 | @:extern 90 | public inline function invert():Void { 91 | x = -x; 92 | y = -y; 93 | } 94 | 95 | @:extern 96 | public inline function cross(vec:Vec2):FastFloat { 97 | return x * vec.y - y * vec.x; 98 | } 99 | 100 | @:extern 101 | public inline function dot(vec:Vec2):FastFloat { 102 | return x * vec.x + y * vec.y; 103 | } 104 | 105 | @:extern 106 | public inline function angle(vec:Vec2):FastFloat { 107 | var theta1:FastFloat = Math.atan2(y, x); 108 | var theta2:FastFloat = Math.atan2(vec.y, vec.x); 109 | var dtheta = theta2 - theta1; 110 | 111 | var twoPI = Math.PI * 2; 112 | 113 | while (dtheta > Math.PI) dtheta -= twoPI; 114 | while (dtheta < -Math.PI) dtheta += twoPI; 115 | 116 | return dtheta; 117 | } 118 | 119 | @:extern 120 | public inline function normalize():Vec2 { 121 | if(length == 0){ 122 | x = 1; 123 | return this; 124 | } 125 | 126 | var len = length; 127 | x /= len; 128 | y /= len; 129 | 130 | return this; 131 | } 132 | 133 | @:extern 134 | public inline function truncate(max:Float):Vec2 { 135 | length = Math.min(max, length); 136 | return this; 137 | } 138 | 139 | @:extern 140 | public inline function transform(matrix:Matrix):Vec2 { 141 | var vec = clone(); 142 | 143 | vec.x = x * matrix._00 + y * matrix._10 + matrix._20; 144 | vec.y = x * matrix._01 + y * matrix._11 + matrix._21; 145 | 146 | return vec; 147 | } 148 | 149 | @:extern 150 | public inline function transformBy(matrix:Matrix):Vec2 { 151 | var xx = x; 152 | var yy = y; 153 | 154 | x = xx * matrix._00 + yy * matrix._10 + matrix._20; 155 | y = xx * matrix._01 + yy * matrix._11 + matrix._21; 156 | 157 | return this; 158 | } 159 | 160 | @:extern 161 | public inline function toString():String { 162 | return "Vec2(x: " + x + ", y: " + y + ")"; 163 | } 164 | 165 | @:extern 166 | inline function get_length():FastFloat { 167 | return Math.sqrt(x * x + y * y); 168 | } 169 | 170 | @:extern 171 | inline function set_length(value:FastFloat):FastFloat { 172 | var angle = Math.atan2(y, x); 173 | 174 | x = Math.cos(angle) * value; 175 | y = Math.sin(angle) * value; 176 | 177 | if (Math.abs(x) < 0.00000001) x = 0; 178 | if (Math.abs(y) < 0.00000001) y = 0; 179 | 180 | return value; 181 | } 182 | 183 | @:extern 184 | inline function get_x():FastFloat { 185 | return this.x; 186 | } 187 | 188 | @:extern 189 | inline function set_x(value:FastFloat):FastFloat { 190 | return this.x = value; 191 | } 192 | 193 | @:extern 194 | inline function get_y():FastFloat { 195 | return this.y; 196 | } 197 | 198 | @:extern 199 | inline function set_y(value:FastFloat):FastFloat { 200 | return this.y = value; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /kala/math/Vec2T.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | /** 6 | * Used for transformations around point. 7 | */ 8 | class Vec2T { 9 | 10 | public var ox:FastFloat; 11 | public var oy:FastFloat; 12 | 13 | public var x:FastFloat; 14 | public var y:FastFloat; 15 | 16 | public inline function new(x:FastFloat = 0, y:FastFloat = 0, originX:FastFloat = 0, originY:FastFloat = 0) { 17 | this.x = x; 18 | this.y = y; 19 | 20 | this.ox = originX; 21 | this.oy = originY; 22 | } 23 | 24 | @:extern 25 | public inline function set(x:FastFloat = 0, y:FastFloat = 0, originX:FastFloat = 0, originY:FastFloat = 0):Void { 26 | this.x = x; 27 | this.y = y; 28 | 29 | this.ox = originX; 30 | this.oy = originY; 31 | } 32 | 33 | @:extern 34 | public inline function copy(vec:Vec2T):Void { 35 | this.x = vec.x; 36 | this.y = vec.y; 37 | } 38 | 39 | @:extern 40 | public inline function clone():Vec2T { 41 | return new Vec2T(x, y, ox, oy); 42 | } 43 | 44 | @:extern 45 | public inline function setXY(x:FastFloat = 0, y:FastFloat = 0):Vec2T { 46 | this.x = x; 47 | this.y = y; 48 | 49 | return this; 50 | } 51 | 52 | @:extern 53 | public inline function setOrigin(originX:FastFloat = 0, originY:FastFloat = 0):Vec2T { 54 | this.ox = originX; 55 | this.oy = originY; 56 | 57 | return this; 58 | } 59 | 60 | @:extern 61 | public inline function setXBetween(x1:FastFloat, x2:FastFloat, xpercent:Int = 50):Vec2T { 62 | this.x = x1 + (x2 - x1) * xpercent / 100; 63 | return this; 64 | } 65 | 66 | @:extern 67 | public inline function setYBetween(y1:FastFloat, y2:FastFloat, ypercent:Int = 50):Vec2T { 68 | this.y = y1 + (y2 - y1) * ypercent / 100; 69 | return this; 70 | } 71 | 72 | @:extern 73 | public inline function setXYBetween( 74 | x1:FastFloat, y1:FastFloat, x2:FastFloat, y2:FastFloat, 75 | xpercent:Int = 50, ypercent:Int = 50 76 | ):Vec2T { 77 | this.x = x1 + (x2 - x1) * xpercent / 100; 78 | this.y = y1 + (y2 - y1) * ypercent / 100; 79 | 80 | return this; 81 | } 82 | 83 | @:extern 84 | public inline function move(x:FastFloat, y:FastFloat):Void { 85 | this.x += x; 86 | this.y += y; 87 | } 88 | 89 | @:extern 90 | public inline function moveOrigin(ox:FastFloat, oy:FastFloat):Void { 91 | this.ox += ox; 92 | this.oy += oy; 93 | } 94 | 95 | public inline function toString():String { 96 | return "Vec2T(x: " + x + ", y: " + y + ", ox: " + ox + ", oy: " + oy + ")" ; 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /kala/math/Velocity.hx: -------------------------------------------------------------------------------- 1 | package kala.math; 2 | 3 | import kha.FastFloat; 4 | 5 | class Velocity { 6 | 7 | var _x:FastFloat; 8 | var _y:FastFloat; 9 | public var x(get, set):FastFloat; 10 | public var y(get, set):FastFloat; 11 | 12 | var _angle:FastFloat; 13 | var _speed:FastFloat; 14 | public var angle(get, set):FastFloat; 15 | public var speed(get, set):FastFloat; 16 | 17 | private var _angleUpdated:Bool; 18 | private var _speedUpdated:Bool; 19 | 20 | public inline function new(x:FastFloat = 0, y:FastFloat = 0) { 21 | _x = x; 22 | _y = y; 23 | 24 | _angleUpdated = false; 25 | _speedUpdated = false; 26 | } 27 | 28 | @:extern 29 | public inline function set(x:FastFloat = 0, y:FastFloat = 0):Velocity { 30 | this.x = x; 31 | this.y = y; 32 | return this; 33 | } 34 | 35 | @:extern 36 | public inline function setAngleSpeed(angle:FastFloat, speed:FastFloat):Velocity { 37 | this.angle = angle; 38 | this.speed = speed; 39 | return this; 40 | } 41 | 42 | @:extern 43 | public inline function clone():Velocity { 44 | return new Velocity(x, y); 45 | } 46 | 47 | inline function get_x():FastFloat { 48 | return _x; 49 | } 50 | 51 | inline function set_x(value:FastFloat):FastFloat { 52 | _angleUpdated = _speedUpdated = false; 53 | return _x = value; 54 | } 55 | 56 | inline function get_y():FastFloat { 57 | return _y; 58 | } 59 | 60 | inline function set_y(value:FastFloat):FastFloat { 61 | _angleUpdated = _speedUpdated = false; 62 | return _y = value; 63 | } 64 | 65 | inline function get_angle():FastFloat { 66 | if (_angleUpdated) return _angle; 67 | _angleUpdated = true; 68 | return _angle = Mathf.deg(Math.atan2(y, x)); 69 | } 70 | 71 | inline function set_angle(value:FastFloat):FastFloat { 72 | var rad = Mathf.rad(value); 73 | _x = speed * Math.cos(rad); 74 | _y = speed * Math.sin(rad); 75 | _angleUpdated = true; 76 | return _angle = value; 77 | } 78 | 79 | inline function get_speed():FastFloat { 80 | if (_speedUpdated) return _speed; 81 | _speedUpdated = true; 82 | return _speed = x / Math.cos(Mathf.rad(angle)); 83 | } 84 | 85 | inline function set_speed(value:FastFloat):FastFloat { 86 | var rad = Mathf.rad(angle); 87 | _x = value * Math.cos(rad); 88 | _y = value * Math.sin(rad); 89 | _speedUpdated = true; 90 | return _speed = value; 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /kala/math/color/BlendMode.hx: -------------------------------------------------------------------------------- 1 | package kala.math.color; 2 | 3 | import kha.FastFloat; 4 | 5 | enum BlendMode { 6 | 7 | ALPHA; 8 | ADD; 9 | SUB; 10 | REVERSE_SUB; 11 | MULTI; 12 | MULTI_2X; 13 | SET(src:BlendFactor, dest:BlendFactor, opt:BlendOpt); 14 | 15 | } 16 | 17 | enum BlendOpt { 18 | 19 | ADD; 20 | SUB; 21 | REVERSE_SUB; 22 | MAX; 23 | MIN; 24 | 25 | } 26 | 27 | enum BlendFactor { 28 | 29 | ZERO; 30 | ONE; 31 | 32 | SRC_ALPHA; 33 | INV_SRC_ALPHA; 34 | 35 | SRC_COLOR; 36 | INV_SRC_COLOR; 37 | 38 | DEST_ALPHA; 39 | INV_DEST_ALPHA; 40 | 41 | DEST_COLOR; 42 | INV_DEST_COLOR; 43 | 44 | SRC_ALPHA_SATURATION; 45 | DEST_ALPHA_SATURATION; 46 | 47 | SET(a:FastFloat, r:FastFloat, g:FastFloat, b:FastFloat); 48 | 49 | } -------------------------------------------------------------------------------- /kala/math/shapes/Triangle.hx: -------------------------------------------------------------------------------- 1 | package kala.math.shapes; 2 | 3 | import kala.math.Vec2; 4 | import kha.math.Matrix; 5 | 6 | class Triangle { 7 | 8 | public var p1:Vec2; 9 | public var p2:Vec2; 10 | public var p3:Vec2; 11 | 12 | public inline function new(p1:Vec2, p2:Vec2, p3:Vec2) { 13 | this.p1 = p1; 14 | this.p2 = p2; 15 | this.p3 = p3; 16 | } 17 | 18 | @:extern 19 | public inline function transform(matrix:Matrix):Triangle { 20 | return new Triangle(p1.transform(matrix), p2.transform(matrix), p3.transform(matrix)); 21 | } 22 | 23 | @:extern 24 | public inline function transformBy(matrix:Matrix):Triangle { 25 | p1.transformBy(matrix); 26 | p2.transformBy(matrix); 27 | p3.transformBy(matrix); 28 | 29 | return this; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /kala/objects/group/Group.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.group; 2 | 3 | import kala.DrawingData; 4 | import kala.math.color.BlendMode; 5 | import kala.math.Matrix; 6 | import kala.objects.group.View; 7 | import kala.objects.Object; 8 | import kala.math.color.Color; 9 | import kha.Canvas; 10 | import kha.FastFloat; 11 | import kha.Image; 12 | import kha.graphics2.ImageScaleQuality; 13 | 14 | typedef GenericGroup = Group; 15 | 16 | interface IGroup extends IObject { 17 | 18 | public var transformationEnable:Bool; 19 | 20 | public var colorBlendMode:BlendMode; 21 | public var colorAlphaBlendMode:BlendMode; 22 | 23 | public var timeScale:FastFloat; 24 | 25 | public var views(default, null):Array; 26 | 27 | public function addView(view:View, pos:Int = -1):Void; 28 | public function removeView(view:View, splice:Bool = false):View; 29 | 30 | private function _add(obj:Object, pos:Int = -1):Void; 31 | private function _remove(obj:Object, spilce:Bool = false):Void; 32 | 33 | } 34 | 35 | @:access(kala.math.color.Color) 36 | class Group extends Object implements IGroup { 37 | 38 | public var transformationEnable:Bool; 39 | 40 | public var colorBlendMode:BlendMode; 41 | public var colorAlphaBlendMode:BlendMode; 42 | 43 | public var factoryFunction:Void->T; 44 | 45 | public var members(default, null):Array = new Array(); 46 | public var views(default, null):Array = new Array(); 47 | 48 | public function new(transformationEnable:Bool = false, ?factoryFunction:Void->T) { 49 | super(); 50 | isGroup = true; 51 | this.transformationEnable = transformationEnable; 52 | this.factoryFunction = factoryFunction; 53 | } 54 | 55 | override public function reset(resetBehaviors:Bool = false):Void { 56 | super.reset(resetBehaviors); 57 | color = Color.WHITE; 58 | colorBlendMode = BlendMode.MULTI_2X; 59 | colorAlphaBlendMode = null; 60 | } 61 | 62 | override public function destroy(destroyBehaviors:Bool = true):Void { 63 | super.destroy(destroyBehaviors); 64 | 65 | while (members.length > 0) members.pop().destroy(destroyBehaviors); 66 | while (views.length > 0) views.pop().destroy(destroyBehaviors); 67 | 68 | members = null; 69 | views = null; 70 | } 71 | 72 | override public function update(elapsed:FastFloat):Void { 73 | var i = 0; 74 | var child:T; 75 | while (i < members.length) { 76 | child = members[i]; 77 | 78 | if (child == null) { 79 | members.splice(i, 1); 80 | continue; 81 | } 82 | 83 | if (child.alive && child.active) { 84 | child.callUpdate(child.groupTimeScaleSkipped ? originalDelta : elapsed); 85 | } 86 | 87 | i++; 88 | } 89 | 90 | i = 0; 91 | var view:View; 92 | while (i < views.length) { 93 | view = views[i]; 94 | 95 | if (view == null) { 96 | views.splice(i, 1); 97 | continue; 98 | } 99 | 100 | if (view.alive && view.active) { 101 | view.callUpdate(view.groupTimeScaleSkipped ? originalDelta : elapsed); 102 | } 103 | 104 | i++; 105 | } 106 | } 107 | 108 | override public function draw(data:DrawingData, canvas:Canvas):Void { 109 | var g2 = canvas.g2; 110 | 111 | if (transformationEnable) { 112 | if (data.transformation == null) data.transformation = _cachedDrawingMatrix = matrix; 113 | else data.transformation = _cachedDrawingMatrix = data.transformation.multmat(matrix); 114 | 115 | if (data.color == null) { 116 | data.color = color; 117 | } else { 118 | data.color = Color.getBlendColor(color, data.color, data.colorBlendMode, data.colorAlphaBlendMode); 119 | } 120 | 121 | data.colorBlendMode = colorBlendMode; 122 | data.colorAlphaBlendMode = colorAlphaBlendMode; 123 | 124 | data.opacity = opacity * data.opacity; 125 | } 126 | 127 | if (antialiasing) data.antialiasing = true; 128 | 129 | if (views.length == 0) { 130 | for (child in members) { 131 | if (child == null) continue; 132 | 133 | if (child.alive && child.isVisible()) { 134 | child.callDraw(data, canvas); 135 | } 136 | } 137 | } else { 138 | g2.end(); 139 | 140 | var viewBuffer:Image; 141 | var matrix:Matrix; 142 | 143 | for (view in views) { 144 | if (view == null) continue; 145 | 146 | viewBuffer = view.viewBuffer; 147 | 148 | if (data.transformation == null) { 149 | data.transformation = Matrix.translation( 150 | -view.viewport.x, 151 | -view.viewport.y 152 | ); 153 | } else { 154 | data.transformation = data.transformation.multmat( 155 | Matrix.translation( -view.viewport.x, -view.viewport.y) 156 | ); 157 | } 158 | 159 | viewBuffer.g2.begin(true, view.transparent ? 0 : (255 << 24 | view.bgColor)); 160 | for (child in members) { 161 | if (child == null) continue; 162 | 163 | if (child.alive && child.isVisible()) { 164 | child.callDraw(data, viewBuffer); 165 | } 166 | } 167 | viewBuffer.g2.end(); 168 | } 169 | 170 | g2.begin(false); 171 | 172 | data.transformation = data.transformation; 173 | 174 | for (view in views) { 175 | if (view == null) continue; 176 | 177 | if (view.alive && view.isVisible()) { 178 | view.callDraw(data, canvas); 179 | } 180 | } 181 | } 182 | } 183 | 184 | override function callDraw(data:DrawingData, canvas:Canvas):Void { 185 | super.callDraw(data.clone(), canvas); 186 | } 187 | 188 | public function createAlive():T { 189 | for (obj in members) { 190 | if (!obj.alive) { 191 | obj.revive(); 192 | return obj; 193 | } 194 | } 195 | 196 | if (factoryFunction != null) { 197 | var obj = factoryFunction(); 198 | add(obj); 199 | return obj; 200 | } 201 | 202 | return null; 203 | } 204 | 205 | public function add(obj:T, pos:Int = -1):Void { 206 | if (members.indexOf(obj) != -1) return; 207 | 208 | if (pos == -1) members.push(obj); 209 | else members.insert(pos, obj); 210 | } 211 | 212 | public function swap(swappedObj:T, obj:T):Bool { 213 | var index = members.indexOf(swappedObj); 214 | 215 | if (index == -1) return false; 216 | 217 | members[index] = obj; 218 | swappedObj.firstFrameExecuted = false; 219 | 220 | return true; 221 | } 222 | 223 | public function remove(obj:T, splice:Bool = false):T { 224 | var index = members.indexOf(obj); 225 | 226 | if (index == -1) return null; 227 | 228 | if (splice) members.splice(index, 1); 229 | else members[index] = null; 230 | 231 | obj.firstFrameExecuted = false; 232 | 233 | return obj; 234 | } 235 | 236 | /** 237 | * Call kill() on every alive member. 238 | * 239 | * @param killSelf If set to true, will also kill this group. DEFAULT: false 240 | */ 241 | public function killAll(killSelf:Bool = false):Void { 242 | for (member in members) { 243 | if (member.alive) member.kill(); 244 | } 245 | 246 | if (killSelf) kill(); 247 | } 248 | 249 | public function addView(view:View, pos:Int = -1):Void { 250 | if (views.indexOf(view) != -1) return null; 251 | 252 | if (pos == -1) views.push(view); 253 | else views.insert(pos, view); 254 | } 255 | 256 | public function removeView(view:View, splice:Bool = false):View { 257 | var index = views.indexOf(view); 258 | 259 | if (index == -1) return null; 260 | 261 | if (splice) views.splice(index, 1); 262 | else views[index] = null; 263 | 264 | view.firstFrameExecuted = false; 265 | 266 | return view; 267 | } 268 | 269 | public function countAlive():Int { 270 | var c = 0; 271 | for (member in members) if (member.alive) c++; 272 | return c; 273 | } 274 | 275 | public function countDead():Int { 276 | var c = 0; 277 | for (member in members) if (!member.alive) c++; 278 | return c; 279 | } 280 | 281 | public inline function iterator():Iterator { 282 | return members.iterator(); 283 | } 284 | 285 | // 286 | 287 | @:noCompletion 288 | function _add(obj:Object, pos:Int = -1):Void { 289 | add(cast obj, pos); 290 | } 291 | 292 | @:noCompletion 293 | function _remove(obj:Object, spilce:Bool = false):Void { 294 | remove(cast obj, spilce); 295 | } 296 | 297 | } -------------------------------------------------------------------------------- /kala/objects/group/View.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.group; 2 | 3 | import kala.DrawingData; 4 | import kala.math.Random; 5 | import kala.math.Vec2; 6 | import kala.math.Vec2T; 7 | import kala.objects.Object; 8 | import kala.math.color.Color; 9 | import kala.math.Rect; 10 | import kala.util.Axes; 11 | import kha.Canvas; 12 | import kha.FastFloat; 13 | import kha.graphics2.ImageScaleQuality; 14 | import kha.Image; 15 | import kha.graphics4.DepthStencilFormat; 16 | 17 | @:access(kala.objects.group.Group) 18 | class View extends Object { 19 | 20 | public var viewBuffer(default, null):Image; 21 | 22 | public var viewport(default, null):Rect; 23 | 24 | public var halign:Null; 25 | public var valign:Null; 26 | 27 | public var scaleMode:ScaleMode; 28 | 29 | public var shaking(get, never):Bool; 30 | public var shakeLeft:Int; 31 | public var shakeIntensity:FastFloat; 32 | public var shakeDelay:FastFloat; 33 | public var shakeAxes:Axes; 34 | 35 | private var _shakeOffsetX:FastFloat; 36 | private var _shakeOffsetY:FastFloat; 37 | private var _shakeDelayTimeLeft:FastFloat; 38 | private var _onShakeCompleteCB:Void->Void; 39 | 40 | /** 41 | * Background color in RGB fortmat. 42 | */ 43 | public var bgColor:UInt; 44 | 45 | /** 46 | * If true, will be fully transparent. 47 | */ 48 | public var transparent:Bool; 49 | 50 | public function new( 51 | viewX:FastFloat, viewY:FastFloat, 52 | viewWidth:UInt, viewHeight:UInt, 53 | ?antiAliasingSamples:UInt = 1 54 | ) { 55 | super(); 56 | viewBuffer = Image.createRenderTarget( 57 | Std.int(viewWidth), Std.int(viewHeight), null, 58 | DepthStencilFormat.NoDepthAndStencil, antiAliasingSamples 59 | ); 60 | 61 | viewBuffer.g2.imageScaleQuality = ImageScaleQuality.Low; 62 | 63 | 64 | viewport = new Rect(viewX, viewY, viewWidth, viewHeight); 65 | } 66 | 67 | override public function reset(resetBehaviors:Bool = false):Void { 68 | super.reset(resetBehaviors); 69 | bgColor = 0; 70 | transparent = true; 71 | scaleMode = NONE; 72 | _shakeOffsetX = _shakeOffsetY = 0; 73 | } 74 | 75 | override public function destroy(destroyBehaviors:Bool = true):Void { 76 | super.destroy(destroyBehaviors); 77 | 78 | viewBuffer.unload(); 79 | viewBuffer = null; 80 | 81 | viewport = null; 82 | } 83 | 84 | override public function update(elapsed:FastFloat):Void { 85 | super.update(elapsed); 86 | updateShake(elapsed); 87 | } 88 | 89 | override public function draw(data:DrawingData, canvas:Canvas):Void { 90 | var cw = canvas.width; 91 | var ch = canvas.height; 92 | 93 | var w:FastFloat = viewport.width = viewBuffer.width; 94 | var h:FastFloat = viewport.height = viewBuffer.height; 95 | 96 | switch(scaleMode) { 97 | 98 | case EXACT: 99 | scale.setXY(cw / w, ch / h); 100 | w = cw; 101 | h = ch; 102 | 103 | case RATIO: 104 | var hs = cw / w; 105 | var vs = ch / h; 106 | 107 | var s = Math.min(hs, vs); 108 | scale.setXY(s, s); 109 | 110 | w *= s; 111 | h *= s; 112 | 113 | case RATIO_FILL: 114 | var hs = cw / w; 115 | var vs = ch / h; 116 | 117 | var s = Math.max(hs, vs); 118 | scale.setXY(s, s); 119 | 120 | w *= s; 121 | h *= s; 122 | 123 | case FIXED(hpercent, vpercent): 124 | scale.setXY((cw * hpercent) / w, (ch * vpercent) / h); 125 | 126 | w *= scale.x; 127 | h *= scale.y; 128 | 129 | case NONE: 130 | 131 | } 132 | 133 | applyDrawingData(data, canvas); 134 | 135 | if (halign != null) { 136 | canvas.g2.transformation._20 = (cw - w) * halign; 137 | } 138 | 139 | if (valign != null) { 140 | canvas.g2.transformation._21 = (ch - h) * valign; 141 | } 142 | 143 | canvas.g2.transformation._20 += _shakeOffsetX; 144 | canvas.g2.transformation._21 += _shakeOffsetY; 145 | _cachedDrawingMatrix = canvas.g2.transformation; 146 | 147 | canvas.g2.drawImage(viewBuffer, 0, 0); 148 | } 149 | 150 | /** 151 | * Project a point from this view to its viewport. 152 | * Only works when this view is visible. 153 | */ 154 | public inline function project(x:FastFloat, y:FastFloat):Vec2 { 155 | return new Vec2(x, y).transformBy(_cachedDrawingMatrix.inverse()); 156 | } 157 | 158 | public inline function setAlignScaleMode(halign:FastFloat, valign:FastFloat, scaleMode:ScaleMode):View { 159 | this.scaleMode = scaleMode; 160 | this.halign = halign; 161 | this.valign = valign; 162 | 163 | return this; 164 | } 165 | 166 | public inline function setCenterScaleMode(scaleMode:ScaleMode):View { 167 | this.scaleMode = scaleMode; 168 | halign = 0.5; 169 | valign = 0.5; 170 | position.setOrigin(0, 0); 171 | 172 | return this; 173 | } 174 | 175 | public inline function shake( 176 | intensity:FastFloat, duration:FastFloat, delay:FastFloat = 0, 177 | ?onCompleteCB:Void->Void, ?axes:Axes 178 | ):Void { 179 | shakeIntensity = intensity; 180 | shakeLeft = Std.int(duration / (delay + 1)); 181 | shakeDelay = delay; 182 | _shakeDelayTimeLeft = 0; 183 | shakeAxes = axes == null ? Axes.XY : axes; 184 | } 185 | 186 | override function get_width():FastFloat { 187 | return viewBuffer.width; 188 | } 189 | 190 | override function get_height():FastFloat { 191 | return viewBuffer.height; 192 | } 193 | 194 | inline function updateShake(elapsed:FastFloat):Void { 195 | if (shakeLeft > 0) { 196 | if (_shakeDelayTimeLeft > 0) _shakeDelayTimeLeft -= elapsed; 197 | else { 198 | _shakeDelayTimeLeft = shakeDelay; 199 | 200 | if (shakeAxes != Axes.Y) { 201 | _shakeOffsetX = _shakeOffsetX < 0 ? 202 | Random.float(0, shakeIntensity) : 203 | Random.float(-shakeIntensity, 0); 204 | } 205 | 206 | if (shakeAxes != Axes.X) { 207 | _shakeOffsetY = _shakeOffsetY < 0 ? 208 | Random.float(0, shakeIntensity) : 209 | Random.float(-shakeIntensity, 0); 210 | } 211 | 212 | shakeLeft--; 213 | 214 | if (shakeLeft == 0) { 215 | _shakeOffsetX = _shakeOffsetY = 0; 216 | if (_onShakeCompleteCB != null) _onShakeCompleteCB(); 217 | } 218 | } 219 | } 220 | } 221 | 222 | inline function get_shaking():Bool { 223 | return shakeLeft > 0; 224 | } 225 | 226 | } 227 | 228 | enum ScaleMode { 229 | 230 | NONE; 231 | EXACT; 232 | RATIO; 233 | RATIO_FILL; 234 | FIXED(hpercent:FastFloat, vpercent:FastFloat); 235 | 236 | } -------------------------------------------------------------------------------- /kala/objects/shapes/Circle.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.shapes; 2 | 3 | import kala.DrawingData; 4 | import kala.math.color.Color; 5 | import kala.math.Vec2; 6 | import kha.Canvas; 7 | import kha.FastFloat; 8 | import kha.graphics2.Graphics; 9 | 10 | class Circle extends Shape { 11 | 12 | public var radius(default, set):FastFloat; 13 | public var segments(default, set):Int; 14 | 15 | private var _vertices:Array = new Array(); 16 | 17 | public function new(radius:FastFloat, fill:Bool = true, outline:Bool = false) { 18 | super(fill, outline); 19 | this.radius = radius; 20 | } 21 | 22 | override public function reset(resetBehaviors:Bool = false):Void { 23 | super.reset(resetBehaviors); 24 | segments = 0; 25 | } 26 | 27 | override public function destroy(destroyBehaviors:Bool = true):Void { 28 | super.destroy(destroyBehaviors); 29 | _vertices = null; 30 | } 31 | 32 | override public function draw(data:DrawingData, canvas:Canvas):Void { 33 | applyDrawingData(data, canvas); 34 | 35 | applyFillDrawingData(); 36 | drawFill(canvas.g2); 37 | 38 | applyLineDrawingData(); 39 | drawOutline(canvas.g2); 40 | } 41 | 42 | function drawFill(g2:Graphics):Void { 43 | #if sys_html5 44 | if (kha.SystemImpl.gl == null) { 45 | var g:kha.js.CanvasGraphics = cast g2; 46 | g.fillCircle(0, 0, radius); 47 | return; 48 | } 49 | #end 50 | 51 | if (segments <= 0) { 52 | segments = Math.floor(10 * Math.sqrt(radius)); 53 | } 54 | 55 | var p1:Vec2; 56 | var p2:Vec2; 57 | for (i in 0..._vertices.length - 1) { 58 | p1 = _vertices[i]; 59 | p2 = _vertices[i + 1]; 60 | g2.fillTriangle(p1.x, p1.y, p2.x, p2.y, 0, 0); 61 | } 62 | 63 | p1 = _vertices[_vertices.length - 1]; 64 | p2 = _vertices[0]; 65 | g2.fillTriangle(p1.x, p1.y, p2.x, p2.y, 0, 0); 66 | } 67 | 68 | function drawOutline(g2:Graphics):Void { 69 | #if sys_html5 70 | if (kha.SystemImpl.gl == null) { 71 | var g:kha.js.CanvasGraphics = cast g2; 72 | radius -= lineStrenght / 2; // Reduce radius to fit the line thickness within image width / height. 73 | g.drawCircle(0, 0, radius, lineStrenght); 74 | return; 75 | } 76 | #end 77 | 78 | var p1:Vec2; 79 | var p2:Vec2; 80 | for (i in 0..._vertices.length - 1) { 81 | p1 = _vertices[i]; 82 | p2 = _vertices[i + 1]; 83 | g2.drawLine(p1.x, p1.y, p2.x, p2.y, lineStrenght); 84 | } 85 | 86 | p1 = _vertices[_vertices.length - 1]; 87 | p2 = _vertices[0]; 88 | g2.drawLine(p1.x, p1.y, p2.x, p2.y, lineStrenght); 89 | } 90 | 91 | function updateVertices():Void { 92 | var segments = this.segments; 93 | if (segments <= 0) { 94 | segments = Math.floor(10 * Math.sqrt(radius)); 95 | } 96 | 97 | if (_vertices.length > segments) _vertices.splice(0, _vertices.length - segments); 98 | 99 | var theta = 2 * Math.PI / segments; 100 | var c = Math.cos(theta); 101 | var s = Math.sin(theta); 102 | 103 | var x = radius; 104 | var y:FastFloat = 0; 105 | var t:FastFloat; 106 | 107 | var point:Vec2; 108 | 109 | for (i in 0...segments) { 110 | if (i < _vertices.length) { 111 | point = _vertices[i]; 112 | } else { 113 | point = new Vec2(); 114 | _vertices.push(point); 115 | } 116 | 117 | point.set(x, y); 118 | 119 | t = x; 120 | x = c * x - s * y; 121 | y = c * y + s * t; 122 | } 123 | } 124 | 125 | function set_radius(value:FastFloat):FastFloat { 126 | radius = value; 127 | updateVertices(); 128 | bufferOriginX = bufferOriginY = radius; 129 | _width = _height = radius * 2; 130 | return value; 131 | } 132 | 133 | function set_segments(value:Int):Int { 134 | segments = value; 135 | updateVertices(); 136 | return value; 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /kala/objects/shapes/Polygon.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.shapes; 2 | 3 | import kala.DrawingData; 4 | import kala.math.color.Color; 5 | import kala.math.Vec2; 6 | import kha.Canvas; 7 | import kha.FastFloat; 8 | import kha.math.Vector2; 9 | 10 | using kha.graphics2.GraphicsExtension; 11 | 12 | class Polygon extends Shape { 13 | 14 | public var vertices(get, set):Array; 15 | var _vertices:Array; 16 | public var vector2Array(default, null):Array; 17 | 18 | public function new(vertices:Array, fill:Bool = true, outline:Bool = false) { 19 | super(fill, outline); 20 | this.vertices = vertices; 21 | } 22 | 23 | override public function destroy(destroyBehaviors:Bool = true):Void { 24 | super.destroy(destroyBehaviors); 25 | vector2Array = null; 26 | _vertices = null; 27 | } 28 | 29 | override public function draw(data:DrawingData, canvas:Canvas):Void { 30 | applyDrawingData(data, canvas); 31 | 32 | applyFillDrawingData(); 33 | canvas.g2.fillPolygon(0, 0, vector2Array); 34 | 35 | applyLineDrawingData(); 36 | canvas.g2.drawPolygon(0, 0, vector2Array, lineStrenght); 37 | } 38 | 39 | inline function get_vertices():Array { 40 | return _vertices; 41 | } 42 | 43 | function set_vertices(value:Array):Array { 44 | _vertices = value; 45 | vector2Array = Vec2.toVector2Array(vertices); 46 | 47 | var minX:FastFloat = 0; 48 | var maxX:FastFloat = 0; 49 | var minY:FastFloat = 0; 50 | var maxY:FastFloat = 0; 51 | 52 | for (v in _vertices) { 53 | if (v.x < minX) minX = v.x; 54 | else if (v.x > maxX) maxX = v.x; 55 | 56 | if (v.y < minY) minY = v.y; 57 | else if (v.y > maxY) maxY = v.y; 58 | } 59 | 60 | _width = Math.abs(maxX - minX); 61 | _height = Math.abs(maxY - minY); 62 | 63 | bufferOriginX = -minX; 64 | bufferOriginY = -minY; 65 | 66 | return value; 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /kala/objects/shapes/Rectangle.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.shapes; 2 | 3 | import kala.DrawingData; 4 | import kha.Canvas; 5 | import kha.FastFloat; 6 | import kala.math.color.Color; 7 | 8 | class Rectangle extends Shape { 9 | 10 | public function new(width:Int, height:Int, fill:Bool = true, outline:Bool = false) { 11 | super(fill, outline); 12 | _width = width; 13 | _height = height; 14 | } 15 | 16 | override public function draw(data:DrawingData, canvas:Canvas):Void { 17 | applyDrawingData(data, canvas); 18 | 19 | applyFillDrawingData(); 20 | canvas.g2.fillRect(0, 0, _width, _height); 21 | 22 | applyLineDrawingData(); 23 | canvas.g2.drawRect(0, 0, _width, _height, lineStrenght); 24 | } 25 | 26 | override function set_width(value:FastFloat):FastFloat { 27 | return _width = value; 28 | } 29 | 30 | override function set_height(value:FastFloat):FastFloat { 31 | return _height = value; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /kala/objects/shapes/Shape.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.shapes; 2 | 3 | import kala.DrawingData; 4 | import kala.math.color.BlendMode; 5 | import kala.math.color.Color; 6 | import kala.objects.Object; 7 | import kha.Canvas; 8 | import kha.FastFloat; 9 | 10 | @:access(kala.math.color.Color) 11 | class Shape extends Object { 12 | 13 | public var lineStrenght:UInt; 14 | public var lineColor:Color; 15 | public var lineOpacity:FastFloat; 16 | 17 | public var fillColor:Color; 18 | public var fillOpacity:FastFloat; 19 | 20 | /** 21 | * DEFAULT: MULTI_2X 22 | */ 23 | public var colorBlendMode:BlendMode; 24 | public var colorAlphaBlendMode:BlendMode; 25 | 26 | // 27 | 28 | private var _color:Color; 29 | private var _opacity:FastFloat; 30 | private var _canvas:Canvas; 31 | 32 | public function new(fill:Bool = true, outline:Bool = false) { 33 | super(); 34 | 35 | if (fill) fillOpacity = 1; else fillOpacity = 0; 36 | if (outline) lineOpacity = 1; else lineOpacity = 0; 37 | } 38 | 39 | override public function reset(resetBehaviors:Bool = false):Void { 40 | super.reset(resetBehaviors); 41 | 42 | color = Color.WHITE; 43 | 44 | lineStrenght = 1; 45 | lineColor = Color.WHITE; 46 | fillColor = Color.WHITE; 47 | 48 | colorBlendMode = BlendMode.MULTI_2X; 49 | colorAlphaBlendMode = null; 50 | } 51 | 52 | override public function destroy(destroyBehaviors:Bool = true):Void { 53 | super.destroy(destroyBehaviors); 54 | _canvas = null; 55 | } 56 | 57 | override public function isVisible():Bool { 58 | return super.isVisible() && (fillOpacity > 0 || lineOpacity > 0); 59 | } 60 | 61 | override function applyDrawingData(data:DrawingData, canvas:Canvas):Void { 62 | super.applyDrawingData(data, canvas); 63 | _color = canvas.g2.color; 64 | _opacity = canvas.g2.opacity; 65 | _canvas = canvas; 66 | } 67 | 68 | inline function applyFillDrawingData():Void { 69 | _canvas.g2.color = Color.getBlendColor(fillColor, _color, colorBlendMode, colorAlphaBlendMode); 70 | _canvas.g2.opacity = _opacity * fillOpacity; 71 | } 72 | 73 | inline function applyLineDrawingData():Void { 74 | _canvas.g2.color = Color.getBlendColor(lineColor, _color, colorBlendMode, colorAlphaBlendMode); 75 | _canvas.g2.opacity = _opacity * lineOpacity; 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /kala/objects/sprite/BaseButtonSprite.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.sprite; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.EventHandle.CallbackHandle; 5 | import kala.math.Vec2; 6 | import kala.objects.group.View; 7 | import kha.FastFloat; 8 | import kha.Image; 9 | 10 | class BaseButtonSprite extends Sprite { 11 | 12 | public var hovered(default, null):Bool; 13 | public var pushed(get, never):Bool; 14 | 15 | /** 16 | * The second arg is id of touch or mouse button (1 - left, 2 - middle, 3 - right). 17 | */ 18 | public var onPush(default, null):CallbackHandleInt->Void>; 19 | /** 20 | * The second arg is id of touch or mouse button (1 - left, 2 - middle, 3 - right). 21 | */ 22 | public var onRelease(default, null):CallbackHandleInt->Void>; 23 | public var onOver(default, null):CallbackHandleVoid>; 24 | public var onOut(default, null):CallbackHandleVoid>; 25 | 26 | public var onPushRequestFullscreen:Bool; 27 | public var onReleaseRequestFullscreen:Bool; 28 | public var onPushOpenURL:String; 29 | public var onReleaseOpenURL:String; 30 | 31 | public var view:View; 32 | 33 | #if js 34 | public var disableMouseOnMobile:Bool; 35 | #end 36 | 37 | private var _mouseHovered:Bool; 38 | private var _touched:Bool; 39 | 40 | public function new( 41 | ?image:Image, 42 | ?frameX:Int, ?frameY:Int, 43 | ?frameWidth:Int, ?frameHeight:Int, 44 | animated:Bool = false 45 | ) { 46 | super(image, frameX, frameY, frameWidth, frameHeight, animated); 47 | 48 | onPush = addCBHandle(new CallbackHandleInt->Void>()); 49 | onRelease = addCBHandle(new CallbackHandleInt->Void>()); 50 | onOver = addCBHandle(new CallbackHandleVoid>()); 51 | onOut = addCBHandle(new CallbackHandleVoid>()); 52 | } 53 | 54 | override public function reset(resetBehaviors:Bool = false):Void { 55 | super.reset(resetBehaviors); 56 | hovered = _mouseHovered = _touched = false; 57 | #if js 58 | disableMouseOnMobile = true; 59 | #end 60 | view = null; 61 | onPushRequestFullscreen = onReleaseRequestFullscreen = false; 62 | onPushOpenURL = onReleaseOpenURL = null; 63 | } 64 | 65 | override public function destroy(destroyBehaviors:Bool = true):Void { 66 | super.destroy(destroyBehaviors); 67 | view = null; 68 | onPush = null; 69 | onRelease = null; 70 | onOver = null; 71 | onOut = null; 72 | } 73 | 74 | override public function update(elapsed:FastFloat):Void { 75 | #if kala_mouse 76 | #if js 77 | if (!disableMouseOnMobile || !Kala.html5.mobile) updateMouse(); 78 | #else 79 | updateMouse(); 80 | #end 81 | #end 82 | 83 | #if kala_touch 84 | updateTouch(); 85 | #end 86 | 87 | if (hovered && !_touched && !_mouseHovered) { 88 | callOnOut(); 89 | } 90 | } 91 | 92 | function test(x:FastFloat, y:FastFloat):Bool { 93 | return false; 94 | } 95 | 96 | #if kala_mouse 97 | function updateMouse():Void { 98 | var p:Vec2; 99 | if (view == null) p = new Vec2(kala.input.Mouse.x, kala.input.Mouse.y); 100 | else p = view.project(kala.input.Mouse.x, kala.input.Mouse.y); 101 | 102 | if (test(p.x, p.y)) { 103 | if (!hovered) { 104 | callOnOver(); 105 | } 106 | 107 | if (kala.input.Mouse.LEFT.justPressed) { 108 | callOnPush(1); 109 | } else if (kala.input.Mouse.LEFT.justReleased) { 110 | callOnRelease(1); 111 | } 112 | 113 | if (kala.input.Mouse.MIDDLE.justPressed) { 114 | callOnPush(2); 115 | } else if (kala.input.Mouse.MIDDLE.justReleased) { 116 | callOnRelease(2); 117 | } 118 | 119 | if (kala.input.Mouse.RIGHT.justPressed) { 120 | callOnPush(3); 121 | } else if (kala.input.Mouse.RIGHT.justReleased) { 122 | callOnRelease(3); 123 | } 124 | 125 | _mouseHovered = true; 126 | } else { 127 | _mouseHovered = false; 128 | } 129 | } 130 | #end 131 | 132 | #if kala_touch 133 | function updateTouch():Void { 134 | _touched = false; 135 | 136 | var p:Vec2; 137 | 138 | for (touch in kala.input.Touch.touches) { 139 | if (view == null) p = new Vec2(touch.x, touch.y); 140 | else p = view.project(touch.x, touch.y); 141 | 142 | if (test(p.x, p.y)) { 143 | if (!hovered) { 144 | callOnOver(); 145 | } 146 | 147 | if (touch.justStarted) { 148 | callOnPush(touch.id); 149 | } else if (touch.justEnded) { 150 | callOnRelease(touch.id); 151 | } 152 | 153 | _touched = true; 154 | } 155 | } 156 | } 157 | #end 158 | 159 | inline function callOnOver():Void { 160 | #if (flash || js) 161 | if (onPushOpenURL != null) { 162 | #if js 163 | Kala.html5.canvas.addEventListener('click', openURLOnPush); 164 | #elseif flash 165 | #end 166 | } 167 | 168 | if (onReleaseOpenURL != null) { 169 | #if js 170 | Kala.html5.canvas.addEventListener('mouseup', openURLOnRelease); 171 | #elseif flash 172 | #end 173 | } 174 | 175 | if (onPushRequestFullscreen) { 176 | #if js 177 | Kala.html5.canvas.addEventListener('click', requestFullscreen); 178 | #elseif flash 179 | #end 180 | } 181 | 182 | if (onReleaseRequestFullscreen) { 183 | #if js 184 | Kala.html5.canvas.addEventListener('mouseup', requestFullscreen); 185 | #elseif flash 186 | #end 187 | } 188 | #end 189 | 190 | hovered = true; 191 | for (callback in onOver) callback.cbFunction(this); 192 | } 193 | 194 | inline function callOnOut():Void { 195 | #if js 196 | Kala.html5.canvas.removeEventListener('click', openURLOnPush); 197 | Kala.html5.canvas.removeEventListener('mouseup', openURLOnRelease); 198 | Kala.html5.canvas.removeEventListener('click', requestFullscreen); 199 | Kala.html5.canvas.removeEventListener('mouseup', requestFullscreen); 200 | #elseif flash 201 | #end 202 | 203 | hovered = false; 204 | for (callback in onOut) callback.cbFunction(this); 205 | } 206 | 207 | inline function callOnPush(id:Int):Void { 208 | #if (!flash && !js) 209 | if (onPushOpenURL != null) Kala.openURL(onPushOpenURL); 210 | if (onPushRequestFullscreen) Kala.requestFullscreen(); 211 | #end 212 | 213 | for (callback in onPush) callback.cbFunction(this, id); 214 | } 215 | 216 | inline function callOnRelease(id:Int):Void { 217 | #if (!flash && !js) 218 | if (onReleaseOpenURL != null) Kala.openURL(onReleaseOpenURL); 219 | if (onReleaseRequestFullscreen) Kala.requestFullscreen(); 220 | #end 221 | 222 | for (callback in onRelease) callback.cbFunction(this, id); 223 | } 224 | 225 | #if (flash || js) 226 | function requestFullscreen():Void { 227 | Kala.requestFullscreen(); 228 | } 229 | 230 | function openURLOnPush():Void { 231 | Kala.openURL(onPushOpenURL); 232 | } 233 | 234 | function openURLOnRelease():Void { 235 | Kala.openURL(onReleaseOpenURL); 236 | } 237 | #end 238 | 239 | function get_pushed():Bool { 240 | return _touched || _mouseHovered; 241 | } 242 | 243 | } 244 | 245 | #end -------------------------------------------------------------------------------- /kala/objects/sprite/BasicButtonSprite.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.sprite; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.behaviors.collision.basic.shapes.CollisionCircle; 5 | import kala.behaviors.collision.basic.shapes.CollisionRectangle; 6 | import kala.behaviors.collision.basic.Collider; 7 | import kha.FastFloat; 8 | import kha.Image; 9 | 10 | class BasicButtonSprite extends BaseButtonSprite { 11 | 12 | public var collider(default, null):Collider; 13 | 14 | public function new( 15 | ?image:Image, 16 | ?frameX:Int, ?frameY:Int, 17 | ?frameWidth:Int, ?frameHeight:Int, 18 | animated:Bool = false 19 | ) { 20 | super(image, frameX, frameY, frameWidth, frameHeight, animated); 21 | collider = new Collider(this); 22 | } 23 | 24 | override public function reset(resetBehaviors:Bool = false):Void { 25 | super.reset(resetBehaviors); 26 | if (collider != null) collider.reset(); 27 | } 28 | 29 | override public function destroy(destroyBehaviors:Bool = true):Void { 30 | super.destroy(destroyBehaviors); 31 | collider = null; 32 | } 33 | 34 | public inline function addCircleMask(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 35 | return collider.addCircle(x, y, radius); 36 | } 37 | 38 | public inline function addRectMask(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionRectangle { 39 | return collider.addRect(x, y, width, height); 40 | } 41 | 42 | public inline function addObjectRectMask():CollisionRectangle { 43 | return collider.addObjectRect(); 44 | } 45 | 46 | override function test(x:FastFloat, y:FastFloat):Bool { 47 | return collider.testPoint(x, y); 48 | } 49 | 50 | } 51 | #end -------------------------------------------------------------------------------- /kala/objects/sprite/ButtonSprite.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.sprite; 2 | 3 | #if (kala_mouse || kala_touch) 4 | import kala.behaviors.collision.transformable.shapes.CollisionCircle; 5 | import kala.behaviors.collision.transformable.shapes.CollisionPolygon; 6 | import kala.behaviors.collision.transformable.shapes.CollisionShape; 7 | import kala.behaviors.collision.transformable.Collider; 8 | import kala.math.Vec2; 9 | import kha.FastFloat; 10 | import kha.Image; 11 | 12 | class ButtonSprite extends BaseButtonSprite { 13 | 14 | public var collider(default, null):Collider; 15 | 16 | public function new( 17 | ?image:Image, 18 | ?frameX:Int, ?frameY:Int, 19 | ?frameWidth:Int, ?frameHeight:Int, 20 | animated:Bool = false 21 | ) { 22 | super(image, frameX, frameY, frameWidth, frameHeight, animated); 23 | collider = new Collider(this); 24 | } 25 | 26 | override public function reset(resetBehaviors:Bool = false):Void { 27 | super.reset(resetBehaviors); 28 | if (collider != null) collider.reset(); 29 | } 30 | 31 | override public function destroy(destroyBehaviors:Bool = true):Void { 32 | super.destroy(destroyBehaviors); 33 | collider = null; 34 | } 35 | 36 | public inline function addCircleMask(x:FastFloat, y:FastFloat, radius:FastFloat):CollisionCircle { 37 | return collider.addCircle(x, y, radius); 38 | } 39 | 40 | public inline function addRectMask(x:FastFloat, y:FastFloat, width:FastFloat, height:FastFloat):CollisionPolygon { 41 | return collider.addRect(x, y, width, height); 42 | } 43 | 44 | public inline function addPolygonMask(x:FastFloat, y:FastFloat, vertices:Array, concave:Bool = false):Array { 45 | return collider.addPolygon(x, y, vertices, concave); 46 | } 47 | 48 | public inline function addObjectRectMask():CollisionPolygon { 49 | return collider.addObjectRect(); 50 | } 51 | 52 | public inline function addShapeMask(shape:CollisionShape):CollisionShape { 53 | return collider.addShape(shape); 54 | } 55 | 56 | override function test(x:FastFloat, y:FastFloat):Bool { 57 | return collider.testPoint(x, y); 58 | } 59 | 60 | } 61 | #end -------------------------------------------------------------------------------- /kala/objects/sprite/Sprite.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.sprite; 2 | 3 | import kala.DrawingData; 4 | import kala.behaviors.display.SpriteAnimation; 5 | import kala.math.color.Color; 6 | import kala.math.Rect; 7 | import kha.Canvas; 8 | import kha.FastFloat; 9 | import kha.Image; 10 | 11 | class Sprite extends Object { 12 | 13 | public var image:Image; 14 | public var frameRect:RectI = new RectI(); 15 | 16 | public var animation(default, null):SpriteAnimation; 17 | 18 | public function new( 19 | ?image:Image, 20 | ?frameX:Int, ?frameY:Int, 21 | ?frameWidth:Int, ?frameHeight:Int, 22 | animated:Bool = false 23 | ) { 24 | super(); 25 | if (image != null) loadImage(image, frameX, frameY, frameWidth, frameHeight); 26 | 27 | if (animated) new SpriteAnimation(this); 28 | } 29 | 30 | override public function destroy(destroyBehaviors:Bool = true):Void { 31 | super.destroy(destroyBehaviors); 32 | image = null; 33 | frameRect = null; 34 | animation = null; 35 | } 36 | 37 | override public function draw(data:DrawingData, canvas:Canvas):Void { 38 | applyDrawingData(data, canvas); 39 | canvas.g2.drawSubImage(image, 0, 0, frameRect.x, frameRect.y, frameRect.width, frameRect.height); 40 | } 41 | 42 | override public function isVisible():Bool { 43 | return super.isVisible() && image != null && frameRect.width > 0 && frameRect.height > 0; 44 | } 45 | 46 | public function loadImage( 47 | image:Image, 48 | ?frameX:Int, ?frameY:Int, 49 | ?frameWidth:Int, ?frameHeight:Int 50 | ):Sprite { 51 | this.image = image; 52 | 53 | if (frameX == null) frameX = 0; 54 | if (frameY == null) frameY = 0; 55 | if (frameWidth == null) frameWidth = image.width; 56 | if (frameHeight == null) frameHeight = image.height; 57 | 58 | frameRect.set(frameX, frameY, frameWidth, frameHeight); 59 | 60 | return this; 61 | } 62 | 63 | public function loadSpriteData(data:SpriteData, ?image:Image, ?animKey:String, ?animDelay:Int = -1):Sprite { 64 | if (image == null) image = data.image; 65 | 66 | if (data.frames.length == 1 && animKey == null) { 67 | var frame = data.frames[0]; 68 | return loadImage(image, frame.x, frame.y, frame.width, frame.height); 69 | } 70 | 71 | if (animation == null) new SpriteAnimation().addTo(this); 72 | 73 | if (animKey == null) animKey = data.key; 74 | 75 | animation.addAnimFromSpriteData(animKey, image, data, animDelay); 76 | 77 | return this; 78 | } 79 | 80 | override function get_width():FastFloat { 81 | return frameRect.width; 82 | } 83 | 84 | override function get_height():FastFloat { 85 | return frameRect.height; 86 | } 87 | 88 | } 89 | 90 | class SpriteData { 91 | 92 | public var key(default, null):String; 93 | public var image:Image; 94 | public var frames:Array; 95 | public var animDelay:Int; 96 | 97 | public inline function new(key:String, image:Image, frames:Array, animDelay:UInt) { 98 | this.key = key; 99 | this.image = image; 100 | this.frames = frames; 101 | this.animDelay = animDelay; 102 | } 103 | 104 | @:extern 105 | public inline function clone():SpriteData { 106 | return new SpriteData(key, image, frames, animDelay); 107 | } 108 | 109 | @:extern 110 | public inline function setImage(image:Image):SpriteData { 111 | this.image = image; 112 | return this; 113 | } 114 | 115 | @:extern 116 | public inline function setFrames(frames:Array):SpriteData { 117 | this.frames = frames; 118 | return this; 119 | } 120 | 121 | @:extern 122 | public inline function setAnimDelay(delay:Int):SpriteData { 123 | animDelay = delay; 124 | return this; 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /kala/objects/text/BasicText.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.text; 2 | 3 | import kala.DrawingData; 4 | import kala.EventHandle.CallbackHandle; 5 | import kala.math.color.Color; 6 | import kala.objects.Object; 7 | import kha.Canvas; 8 | import kha.FastFloat; 9 | 10 | using StringTools; 11 | 12 | class BasicText extends Object { 13 | 14 | var _text:String; 15 | public var text(get, set):String; 16 | 17 | public var font(default, set):Font; 18 | 19 | public var size(default, set):UInt; 20 | public var bold(default, set):Bool; 21 | public var italic:Bool; 22 | public var underlined:Bool; 23 | 24 | public var onTextChange(default, null):CallbackHandleString->Void>; 25 | 26 | public function new(?text:String, ?font:Font, ?size:UInt = 24) { 27 | super(); 28 | 29 | _text = text; 30 | 31 | this.size = size; 32 | this.font = font; 33 | 34 | onTextChange = addCBHandle(new CallbackHandleString->Void>()); 35 | } 36 | 37 | override public function reset(resetBehaviors:Bool = false):Void { 38 | bold = false; 39 | italic = false; 40 | underlined = false; 41 | 42 | super.reset(resetBehaviors); 43 | } 44 | 45 | override public function destroy(destroyBehaviors:Bool = true):Void { 46 | super.destroy(destroyBehaviors); 47 | font = null; 48 | onTextChange = null; 49 | } 50 | 51 | override public function draw(data:DrawingData, canvas:Canvas):Void { 52 | applyDrawingData(data, canvas); 53 | var g2 = canvas.g2; 54 | g2.font = font; 55 | g2.fontSize = size; 56 | g2.drawString(text, 0, 0); 57 | } 58 | 59 | override public function isVisible():Bool { 60 | return super.isVisible() && _text != null && _text.length > 0 && font != null && size > 0; 61 | } 62 | 63 | override function get_width():FastFloat { 64 | return font.getWidth(text, size, bold); 65 | } 66 | 67 | override function get_height():FastFloat { 68 | return font.getHeight(size); 69 | } 70 | 71 | function get_text():String { 72 | return _text; 73 | } 74 | 75 | function set_text(value:String):String { 76 | //value = value.replace('\r', "").replace('\n', ""); 77 | var prvText = _text; 78 | _text = value; 79 | for (callback in onTextChange) callback.cbFunction(this, prvText); 80 | return value; 81 | } 82 | 83 | function set_font(value:Font):Font { 84 | if (value == null) font = Kala.defaultFont; 85 | else font = value; 86 | return font; 87 | } 88 | 89 | function set_size(value:UInt):UInt { 90 | return size = value; 91 | } 92 | 93 | function set_bold(value:Bool):Bool { 94 | return bold = value; 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /kala/objects/text/BitmapFont.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.text; 2 | 3 | class BitmapFont { 4 | 5 | public function new() { 6 | 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /kala/objects/text/Font.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.text; 2 | 3 | import kha.FastFloat; 4 | 5 | @:allow(kala.objects.text.BasicText) 6 | abstract Font(Dynamic) from kha.Font to kha.Font from BitmapFont to BitmapFont { 7 | 8 | public inline function getWidth(str:String, size:UInt, bold:Bool):FastFloat { 9 | if (isBitmapFont()) { 10 | return 0; 11 | } else { 12 | var font:kha.Font = cast this; 13 | return font.width(size, str); 14 | } 15 | } 16 | 17 | public inline function getHeight(size:UInt):FastFloat { 18 | if (isBitmapFont()) { 19 | return 0; 20 | } else { 21 | var font:kha.Font = cast this; 22 | return font.height(size); 23 | } 24 | } 25 | 26 | public inline function getBaseline(size:Int):FastFloat { 27 | if (isBitmapFont()) { 28 | return 0; 29 | } else { 30 | var font:kha.Font = cast this; 31 | return font.baseline(size); 32 | } 33 | } 34 | 35 | public inline function isBitmapFont():Bool { 36 | return Std.is(this, BitmapFont); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /kala/objects/text/Text.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.text; 2 | 3 | import kala.math.color.BlendMode; 4 | import kala.math.Vec2; 5 | import kala.objects.Object; 6 | import kala.math.color.Color; 7 | import kala.objects.text.Text.LineData; 8 | import kha.Assets; 9 | import kha.Canvas; 10 | import kha.FastFloat; 11 | import kha.FontStyle; 12 | import kha.Image; 13 | import kha.graphics2.ImageScaleQuality; 14 | 15 | using kala.util.StringUtil; 16 | using StringTools; 17 | 18 | @:access(kala.math.color.Color) 19 | class Text extends BasicText { 20 | 21 | var _htmlText:String; 22 | public var htmlText(get, set):String; 23 | 24 | public var lineSpacing:Int; 25 | public var fixedLineSpacing:Bool; 26 | 27 | public var align:TextAlign; 28 | 29 | /** 30 | * The width of this text without padding. 31 | */ 32 | public var contentWidth(get, null):FastFloat; 33 | 34 | public var wrapByWord(default, set):Bool; 35 | 36 | public var eolSymbol(default, set):String; 37 | 38 | public var padding:Vec2 = new Vec2(); 39 | 40 | public var borderSize:UInt; 41 | public var borderColor:Color = Color.WHITE; 42 | public var borderOpacity:FastFloat; 43 | 44 | public var bgColor:Color = Color.WHITE; 45 | public var bgOpacity:FastFloat; 46 | 47 | public var textColor:Color = Color.WHITE; 48 | public var textOpacity:FastFloat; 49 | 50 | public var colorBlendMode:BlendMode = BlendMode.ADD; 51 | public var colorAlphaBlendMode:BlendMode = null; 52 | 53 | private var _dirty:Bool; 54 | 55 | private var _lines:Array = new Array(); 56 | 57 | public function new(?text:String, ?font:Font, ?size:UInt = 24, ?width:UInt = 0, ?align:TextAlign) { 58 | super(text, font, size); 59 | 60 | this.width = width; 61 | this.align = align == null ? TextAlign.LEFT : align; 62 | 63 | if (text != null) refreshText(); 64 | } 65 | 66 | override public function reset(resetBehaviors:Bool = false):Void { 67 | super.reset(resetBehaviors); 68 | 69 | _htmlText = null; 70 | 71 | fixedLineSpacing = false; 72 | 73 | align = TextAlign.LEFT; 74 | width = 0; 75 | wrapByWord = true; 76 | eolSymbol = "\n"; 77 | 78 | padding.set(); 79 | 80 | borderSize = 1; 81 | borderColor = Color.WHITE; 82 | borderOpacity = 0; 83 | 84 | bgColor = Color.WHITE; 85 | bgOpacity = 0; 86 | 87 | textColor = Color.WHITE; 88 | textOpacity = 1; 89 | 90 | color = Color.TRANSPARENT; 91 | 92 | colorBlendMode = BlendMode.ADD; 93 | colorAlphaBlendMode = null; 94 | } 95 | 96 | override public function destroy(destroyBehaviors:Bool = true):Void { 97 | super.destroy(destroyBehaviors); 98 | padding = null; 99 | _lines = null; 100 | } 101 | 102 | override public function draw(data:DrawingData, canvas:Canvas):Void { 103 | if (_dirty) { 104 | if (_htmlText == null) refreshText(); 105 | else refreshHTMLText(); 106 | 107 | _dirty = false; 108 | } 109 | 110 | data.color = null; 111 | applyDrawingData(data, canvas); 112 | 113 | var g2 = canvas.g2; 114 | var color:Color = g2.color; 115 | opacity = g2.opacity; 116 | 117 | if (bgOpacity > 0) { 118 | g2.color = Color.getBlendColor(bgColor, color, colorBlendMode, colorAlphaBlendMode); 119 | g2.opacity = opacity * bgOpacity; 120 | g2.fillRect(0, 0, width, height); 121 | } 122 | 123 | if (borderOpacity > 0 && borderSize > 0) { 124 | g2.color = Color.getBlendColor(borderColor, color, colorBlendMode, colorAlphaBlendMode); 125 | g2.opacity = opacity * borderOpacity; 126 | g2.drawRect(0, 0, width, height, borderSize); 127 | } 128 | 129 | var defaultTextColor = Color.getBlendColor(textColor, color, colorBlendMode, colorAlphaBlendMode); 130 | g2.opacity = opacity * textOpacity; 131 | 132 | switch(align) { 133 | case TextAlign.JUSTIFY: 134 | var contentWidth = this.contentWidth; 135 | var line:LineData; 136 | var lineWidth:FastFloat; 137 | var words:Array; 138 | var tx:FastFloat; 139 | var ty:FastFloat = padding.y; 140 | var spaceSize:FastFloat; 141 | 142 | for (i in 0..._lines.length) { 143 | line = _lines[i]; 144 | tx = padding.x; 145 | 146 | words = new Array(); 147 | for (textData in line) { 148 | words = words.concat(textData.text.words(eolSymbol, false, false)); 149 | } 150 | 151 | lineWidth = line.getWidth(true); 152 | 153 | if (lineWidth < contentWidth * 0.8) { 154 | for (textData in line) { 155 | g2.font = textData.font; 156 | g2.fontSize = textData.size; 157 | g2.color = textData.color == null ? defaultTextColor : Color.getBlendColor(textData.color, color, colorBlendMode, colorAlphaBlendMode); 158 | g2.drawString(textData.text, tx, ty); 159 | 160 | tx += textData.getWidth(); 161 | } 162 | } else { 163 | spaceSize = (contentWidth - lineWidth) / (words.length - 1); 164 | 165 | if (_htmlText == null) { 166 | for (word in words) { 167 | g2.font = font; 168 | g2.fontSize = size; 169 | g2.color = defaultTextColor; 170 | g2.drawString(word, tx, ty); 171 | 172 | tx += font.getWidth(word, size, bold) + spaceSize; 173 | } 174 | } else { 175 | for (textData in line) { 176 | for (word in textData.text.words(eolSymbol, false, false)) { 177 | g2.font = textData.font; 178 | g2.fontSize = textData.size; 179 | g2.color = textData.color == null ? defaultTextColor : Color.getBlendColor(textData.color, color, colorBlendMode, colorAlphaBlendMode); 180 | 181 | tx += textData.font.getWidth(word, textData.size, textData.bold) + spaceSize; 182 | } 183 | } 184 | } 185 | } 186 | 187 | ty += line.height + lineSpacing; 188 | } 189 | 190 | default: 191 | var contentWidth = this.contentWidth; 192 | var tx:FastFloat = 0; 193 | var ty:FastFloat = padding.y; 194 | 195 | for (line in _lines) { 196 | if (align.equals(TextAlign.LEFT)) { 197 | tx = 0; 198 | } else if (align.equals(TextAlign.RIGHT)) { 199 | tx = contentWidth - line.width; 200 | } else { 201 | tx = (contentWidth - line.width) / 2; 202 | } 203 | 204 | tx += padding.x; 205 | 206 | for (textData in line) { 207 | g2.font = textData.font; 208 | g2.fontSize = textData.size; 209 | g2.color = textData.color == null ? defaultTextColor : Color.getBlendColor(textData.color, color, colorBlendMode, colorAlphaBlendMode); 210 | g2.drawString(textData.text, tx, ty); 211 | 212 | tx += textData.getWidth(); 213 | } 214 | 215 | ty += line.height + lineSpacing; 216 | } 217 | } 218 | 219 | } 220 | 221 | function refreshText():Void { 222 | _lines.splice(0, _lines.length); 223 | 224 | var array:Array; 225 | 226 | if (wrapByWord) array = text.words(eolSymbol, true, true); 227 | else array = text.chars(eolSymbol); 228 | 229 | _height = 0; 230 | var lineString = ""; 231 | 232 | for (s in array) { 233 | if ( 234 | s != eolSymbol && 235 | (_width == 0 || lineString == "" || font.getWidth(lineString + s, size, bold) <= contentWidth) 236 | ) { 237 | lineString += s; 238 | } else { 239 | var line = new LineData() 240 | .add(new TextData(lineString, font, size, bold, italic, underlined, null)) 241 | .refreshSize(); 242 | _lines.push(line); 243 | 244 | _height += line.height; 245 | 246 | if (s == eolSymbol || s.isSpace(0)) lineString = ""; 247 | else lineString = s; 248 | } 249 | } 250 | 251 | var line = new LineData() 252 | .add(new TextData(lineString, font, size, bold, italic, underlined, color)) 253 | .refreshSize(); 254 | _lines.push(line); 255 | 256 | _height += line.height; 257 | } 258 | 259 | function refreshHTMLText():Void { 260 | 261 | } 262 | 263 | function refreshLineSpacing():Void { 264 | if (fixedLineSpacing || font == null) return; 265 | lineSpacing = Std.int(font.getHeight(size) * 0.15); 266 | } 267 | 268 | override function get_width():FastFloat { 269 | return _width; 270 | } 271 | 272 | override function set_width(value:FastFloat):FastFloat { 273 | _dirty = true; 274 | return _width = value; 275 | } 276 | 277 | override function get_height():FastFloat { 278 | return _height + lineSpacing * (_lines.length - 1) + padding.y * 2; 279 | } 280 | 281 | override function set_text(value:String):String { 282 | var prvText = _text; 283 | 284 | _dirty = true; 285 | _htmlText = null; 286 | _text = value; 287 | 288 | for (callback in onTextChange) callback.cbFunction(this, prvText); 289 | 290 | return value; 291 | } 292 | 293 | override function set_font(value:Font):Font { 294 | _dirty = true; 295 | 296 | super.set_font(value); 297 | refreshLineSpacing(); 298 | 299 | return font; 300 | } 301 | 302 | override function set_size(value:UInt):UInt { 303 | _dirty = true; 304 | 305 | size = value; 306 | refreshLineSpacing(); 307 | 308 | return value; 309 | } 310 | 311 | override function set_bold(value:Bool):Bool { 312 | _dirty = true; 313 | return bold = value; 314 | } 315 | 316 | function get_contentWidth():FastFloat { 317 | return width - padding.x * 2; 318 | } 319 | 320 | function set_eolSymbol(value:String):String { 321 | _dirty = true; 322 | return eolSymbol = value; 323 | } 324 | 325 | function set_wrapByWord(value:Bool):Bool { 326 | _dirty = true; 327 | return wrapByWord = value; 328 | } 329 | 330 | function get_htmlText():String { 331 | return _htmlText == null ? _text : _htmlText; 332 | } 333 | 334 | function set_htmlText(value:String):String { 335 | _dirty = true; 336 | return _htmlText = value; 337 | } 338 | 339 | } 340 | 341 | class TextData { 342 | 343 | public var text:String; 344 | 345 | public var font:Font; 346 | public var size:UInt; 347 | public var bold:Bool; 348 | public var italic:Bool; 349 | public var underlined:Bool; 350 | public var color:Null; 351 | 352 | public var url:String; 353 | 354 | public inline function new( 355 | text:String, 356 | font:Font, size:UInt, bold:Bool, italic:Bool, underlined:Bool, ?color:Color, 357 | ?url:String 358 | ) { 359 | this.text = text; 360 | 361 | this.font = font; 362 | this.size = size; 363 | this.bold = bold; 364 | this.italic = italic; 365 | this.underlined = underlined; 366 | 367 | this.url = url; 368 | } 369 | 370 | @:extern 371 | public inline function getWidth(excludeSpace:Bool = false):FastFloat { 372 | var str:String; 373 | if (excludeSpace) str = text.replace(' ', ''); 374 | else str = text; 375 | return font.getWidth(str, size, bold); 376 | } 377 | 378 | @:extern 379 | public inline function getHeight():FastFloat { 380 | return font.getHeight(size); 381 | } 382 | 383 | } 384 | 385 | @:allow(kala.objects.text.Text) 386 | class LineData { 387 | 388 | private var _texts:Array = new Array(); 389 | 390 | public var width(default, null):FastFloat; 391 | public var height(default, null):FastFloat; 392 | 393 | public function new() { 394 | 395 | } 396 | 397 | public inline function iterator():Iterator { 398 | return _texts.iterator(); 399 | } 400 | 401 | public inline function add(textData:TextData):LineData { 402 | _texts.push(textData); 403 | return this; 404 | } 405 | 406 | public inline function getWidth(excludeSpace:Bool = false):FastFloat { 407 | var w:FastFloat = 0; 408 | 409 | for (text in _texts) { 410 | w += text.getWidth(excludeSpace); 411 | } 412 | 413 | return w; 414 | } 415 | 416 | public inline function refreshWidth(excludeSpace:Bool = false):FastFloat { 417 | return width = getWidth(excludeSpace); 418 | } 419 | 420 | public inline function refreshHeight():FastFloat { 421 | height = 0; 422 | var textHeight:FastFloat = 0; 423 | 424 | for (text in _texts) { 425 | textHeight = text.getHeight(); 426 | if (textHeight > height) height = textHeight; 427 | } 428 | 429 | return height; 430 | } 431 | 432 | public inline function refreshSize():LineData { 433 | refreshWidth(); 434 | refreshHeight(); 435 | return this; 436 | } 437 | 438 | } -------------------------------------------------------------------------------- /kala/objects/text/TextAlign.hx: -------------------------------------------------------------------------------- 1 | package kala.objects.text; 2 | 3 | enum TextAlign { 4 | 5 | LEFT; 6 | RIGHT; 7 | CENTER; 8 | JUSTIFY; 9 | 10 | } -------------------------------------------------------------------------------- /kala/system/HTML5.hx: -------------------------------------------------------------------------------- 1 | package kala.system; 2 | 3 | #if js 4 | import js.Browser; 5 | import js.html.CanvasElement; 6 | import js.html.Node; 7 | import kha.SystemImpl; 8 | 9 | class HTML5 { 10 | 11 | public var mobile(get, never):Bool; 12 | public var canvas(default, null):CanvasElement; 13 | 14 | public function new() { 15 | canvas = cast Browser.document.getElementById('khanvas'); 16 | } 17 | 18 | public function fillPage():Void { 19 | var node = Browser.document.createElement('meta'); 20 | node.setAttribute('name', "viewport"); 21 | node.setAttribute('content', "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"); 22 | Browser.document.head.appendChild(node); 23 | 24 | var window = Browser.window; 25 | 26 | function resizeCanvas() { 27 | canvas.style.width = window.innerWidth; 28 | window.setTimeout(function() { 29 | canvas.style.height = window.innerHeight; 30 | }, 0); 31 | } 32 | 33 | window.addEventListener('resize', resizeCanvas, false); 34 | window.addEventListener('orientationchange', resizeCanvas, false); 35 | 36 | Browser.document.body.style.margin = '0'; 37 | Browser.document.body.style.overflow = 'hidden'; 38 | 39 | canvas.style.position = 'absolute'; 40 | canvas.style.left = canvas.style.top = '0'; 41 | canvas.style.width = canvas.style.height = '100%'; 42 | } 43 | 44 | inline function get_mobile():Bool { 45 | return SystemImpl.mobile; 46 | } 47 | 48 | } 49 | #end -------------------------------------------------------------------------------- /kala/util/Axes.hx: -------------------------------------------------------------------------------- 1 | package kala.util; 2 | 3 | @:enum 4 | abstract Axes(Int) { 5 | 6 | var X = 0; 7 | var Y = 1; 8 | var XY = 2; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /kala/util/StringUtil.hx: -------------------------------------------------------------------------------- 1 | package kala.util; 2 | 3 | class StringUtil { 4 | 5 | public static function chars(string:String, ?eolSymbol:String = '\n'):Array { 6 | var lines = string.split(eolSymbol); 7 | var array = new Array(); 8 | 9 | for (line in lines) { 10 | for (i in 0...line.length) { 11 | array.push(line.charAt(i)); 12 | } 13 | 14 | array.push(eolSymbol); 15 | } 16 | 17 | return array; 18 | } 19 | 20 | public static function words(string:String, ?eolSymbol:String = '\n', ?spaceAsWord:Bool = false, ?lineFeedAsWord:Bool = false):Array { 21 | var words = new Array(); 22 | var lines = string.split(eolSymbol); 23 | var wordsInLine:Array; 24 | var lineIndex = 0; 25 | var wordIndex = 0; 26 | 27 | for (line in lines) { 28 | wordIndex = 0; 29 | wordsInLine = line.split(' '); 30 | 31 | for (word in wordsInLine) { 32 | if (word.length != 0) words.push(word); 33 | if (spaceAsWord && wordIndex < wordsInLine.length - 1) words.push(' '); 34 | wordIndex++; 35 | } 36 | 37 | if (lineFeedAsWord && lineIndex < lines.length - 1) words.push(eolSymbol); 38 | 39 | lineIndex++; 40 | } 41 | 42 | return words; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /kala/util/pool/Pool.hx: -------------------------------------------------------------------------------- 1 | package kala.util.pool; 2 | 3 | class Pool { 4 | 5 | public var factoryFunction:Void->T; 6 | 7 | private var _objects:Array = new Array(); 8 | 9 | public function new(?factoryFunction:Void->T) { 10 | this.factoryFunction = factoryFunction; 11 | } 12 | 13 | public function destroy():Void { 14 | factoryFunction = null; 15 | _objects = null; 16 | } 17 | 18 | public function get():T { 19 | if (_objects.length > 0) return _objects.pop(); 20 | if (factoryFunction != null) return factoryFunction(); 21 | return null; 22 | } 23 | 24 | /** 25 | * Put an object into the pool regardless if the object is already in the pool or not. 26 | */ 27 | public inline function putUnsafe(obj:T):Void { 28 | if (obj != null) _objects.push(obj); 29 | } 30 | 31 | public inline function put(obj:T):Void { 32 | if (obj != null && _objects.indexOf(obj) == -1) { 33 | _objects.push(obj); 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /kala/util/types/Pair.hx: -------------------------------------------------------------------------------- 1 | package kala.util.types; 2 | 3 | class Pair { 4 | 5 | public var a:T1; 6 | public var b:T2; 7 | 8 | public inline function new(a:T1, b:T2) { 9 | this.a = a; 10 | this.b = b; 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /kala/util/types/Trio.hx: -------------------------------------------------------------------------------- 1 | package kala.util.types; 2 | 3 | class Trio { 4 | 5 | public var a:T1; 6 | public var b:T2; 7 | public var c:T3; 8 | 9 | public function new(a:T1, b:T2, c:T3) { 10 | this.a = a; 11 | this.b = b; 12 | this.c = c; 13 | } 14 | 15 | } --------------------------------------------------------------------------------