├── scripts ├── __init__.py ├── collision │ ├── __init__.py │ ├── returnFalse.py │ ├── removeSecond.py │ ├── dragZoneSecond.py │ ├── reverseSecondGrav.py │ ├── killPlayer.py │ ├── getPowerUpZone.py │ ├── getPowerUp.py │ ├── pull2first.py │ ├── playSound.py │ ├── pushSecondWithFirst.py │ ├── changeGravAngle.py │ ├── physicsZone.py │ └── grav2first.py └── serials │ ├── __init__.py │ ├── wheelz.py │ └── KEE.py ├── sprites ├── genatlas.sh ├── Dirt.png ├── Wood3.png ├── arrow.png ├── bgm.jpg ├── ice.png ├── orb.png ├── plank.png ├── sheep.png ├── snow.png ├── wood.png ├── Booster.png ├── Grass1.png ├── Jetpack.png ├── awheel.png ├── circle.png ├── cliffs2.png ├── firefox.png ├── icecube.png ├── square.png ├── wrecker.png ├── Boulder2.png ├── Boulder3.png ├── dirtclod.png ├── emptybox.png ├── face_ball.png ├── face_box.png ├── grassyrock.png ├── magicball.png ├── windows1.png ├── checksphere.png └── concreteplates.png ├── sounds └── bing.wav ├── assets ├── myatlas-0.png ├── myatlas.atlas ├── shaders │ ├── posunicolorshader.glsl │ ├── poscolorshader.glsl │ └── noiseshader.glsl └── glsl │ ├── positionshader.glsl │ ├── positionrotateshader.glsl │ └── positionrotateshadernocol.glsl ├── debugprint.py ├── readme.md ├── util.py ├── serialisation.py ├── todo.txt ├── objscripts.py ├── Spline.py ├── PolyGen.py ├── examples └── pong.json ├── kivented.kv ├── ui_elements.py └── main.py /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/collision/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/serials/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | -------------------------------------------------------------------------------- /sprites/genatlas.sh: -------------------------------------------------------------------------------- 1 | python -m kivy.atlas myatlas 1024 *.png 2 | -------------------------------------------------------------------------------- /sounds/bing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sounds/bing.wav -------------------------------------------------------------------------------- /sprites/Dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Dirt.png -------------------------------------------------------------------------------- /sprites/Wood3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Wood3.png -------------------------------------------------------------------------------- /sprites/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/arrow.png -------------------------------------------------------------------------------- /sprites/bgm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/bgm.jpg -------------------------------------------------------------------------------- /sprites/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/ice.png -------------------------------------------------------------------------------- /sprites/orb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/orb.png -------------------------------------------------------------------------------- /sprites/plank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/plank.png -------------------------------------------------------------------------------- /sprites/sheep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/sheep.png -------------------------------------------------------------------------------- /sprites/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/snow.png -------------------------------------------------------------------------------- /sprites/wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/wood.png -------------------------------------------------------------------------------- /sprites/Booster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Booster.png -------------------------------------------------------------------------------- /sprites/Grass1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Grass1.png -------------------------------------------------------------------------------- /sprites/Jetpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Jetpack.png -------------------------------------------------------------------------------- /sprites/awheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/awheel.png -------------------------------------------------------------------------------- /sprites/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/circle.png -------------------------------------------------------------------------------- /sprites/cliffs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/cliffs2.png -------------------------------------------------------------------------------- /sprites/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/firefox.png -------------------------------------------------------------------------------- /sprites/icecube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/icecube.png -------------------------------------------------------------------------------- /sprites/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/square.png -------------------------------------------------------------------------------- /sprites/wrecker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/wrecker.png -------------------------------------------------------------------------------- /assets/myatlas-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/assets/myatlas-0.png -------------------------------------------------------------------------------- /sprites/Boulder2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Boulder2.png -------------------------------------------------------------------------------- /sprites/Boulder3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/Boulder3.png -------------------------------------------------------------------------------- /sprites/dirtclod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/dirtclod.png -------------------------------------------------------------------------------- /sprites/emptybox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/emptybox.png -------------------------------------------------------------------------------- /sprites/face_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/face_ball.png -------------------------------------------------------------------------------- /sprites/face_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/face_box.png -------------------------------------------------------------------------------- /sprites/grassyrock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/grassyrock.png -------------------------------------------------------------------------------- /sprites/magicball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/magicball.png -------------------------------------------------------------------------------- /sprites/windows1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/windows1.png -------------------------------------------------------------------------------- /sprites/checksphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/checksphere.png -------------------------------------------------------------------------------- /sprites/concreteplates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/KivEntEd/master/sprites/concreteplates.png -------------------------------------------------------------------------------- /scripts/collision/returnFalse.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | def collision_func(space, arbiter): 4 | return False -------------------------------------------------------------------------------- /scripts/collision/removeSecond.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | 4 | def collision_func(space, arbiter): 5 | scripty.gameref.delObjNext(arbiter.shapes[1].body.data) 6 | -------------------------------------------------------------------------------- /scripts/collision/dragZoneSecond.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | 4 | def collision_func(space, arbiter): 5 | second_body = arbiter.shapes[1].body 6 | diff =second_body.velocity 7 | diff.x*=-0.97 8 | diff.y*=-0.97 9 | second_body.apply_impulse(diff) 10 | return False -------------------------------------------------------------------------------- /scripts/collision/reverseSecondGrav.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | import cymunk as cy 3 | 4 | 5 | def collision_func(space, arbiter): 6 | second_body = arbiter.shapes[1].body 7 | diff = cy.Vec2d(space.gravity.x*-0.25,space.gravity.y*-0.25) 8 | second_body.apply_impulse(diff) 9 | return False -------------------------------------------------------------------------------- /scripts/collision/killPlayer.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | import cymunk as cy 4 | from math import * 5 | 6 | #this is a refrence to kiventeds scripting system 7 | scripty = None 8 | 9 | defaults = { 10 | } 11 | 12 | def collision_func(space, arbiter): 13 | #implemented in gameonly 14 | return False -------------------------------------------------------------------------------- /scripts/collision/getPowerUpZone.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | #this is a refrence to kiventeds scripting system 4 | scripty = None 5 | 6 | defaults = { 7 | "type": 'jetpack',#boardboost 8 | "duration": '1.', 9 | } 10 | 11 | def collision_func(space, arbiter): 12 | #implemented in gameonly 13 | return False -------------------------------------------------------------------------------- /scripts/collision/getPowerUp.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | import cymunk as cy 4 | from math import * 5 | 6 | #this is a refrence to kiventeds scripting system 7 | scripty = None 8 | 9 | defaults = { 10 | "type": 'jetpack', 11 | "duration": '1.', 12 | } 13 | 14 | def collision_func(space, arbiter): 15 | #implemented in gameonly 16 | return False -------------------------------------------------------------------------------- /scripts/collision/pull2first.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | import cymunk as cy 3 | #from math import * 4 | 5 | def collision_func(space, arbiter): 6 | first_body = arbiter.shapes[0].body 7 | second_body = arbiter.shapes[1].body 8 | first_pos = first_body.position 9 | second_pos = second_body.position 10 | diff = cy.Vec2d(first_pos.x-second_pos.x,first_pos.y-second_pos.y) 11 | #diff.x*=10 12 | #diff.y*=10 13 | second_body.apply_impulse(diff) 14 | #print diff 15 | return False -------------------------------------------------------------------------------- /scripts/collision/playSound.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | import cymunk as cy 3 | 4 | from kivy.core.audio import SoundLoader, Sound 5 | 6 | sound = SoundLoader.load('sounds/bing.wav') 7 | sound.volume *= 0.2 8 | def collision_func(space, arbiter): 9 | if arbiter.is_first_contact == 1: 10 | second_body = arbiter.shapes[1].body 11 | bv= second_body.velocity 12 | s2 = (bv.x*bv.x+bv.y*bv.y) 13 | #print s2 14 | if s2>1000: 15 | if sound.status == 'play': sound.stop() 16 | sound.play() 17 | return True -------------------------------------------------------------------------------- /debugprint.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | #based on http://stackoverflow.com/a/1620686/445831 4 | import sys 5 | import traceback 6 | 7 | class TracePrints(object): 8 | def __init__(self): 9 | self.stdout = sys.stdout 10 | def write(self, s): 11 | #self.stdout.write(s+"\n") 12 | strs = str(s)+"\n" 13 | self.stdout.write(strs) 14 | stack = traceback.extract_stack() 15 | stackout = stack[-2]#[0:10] 16 | self.stdout.write('File "'+stackout[0]+'", line '+str(stackout[1])+', in '+stackout[2]+'\n') 17 | 18 | sys.stdout =TracePrints() -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | KivEntEd 2 | ================== 3 | 4 | A simple physics/level editor using Kivent with basic support to export Wheelz/XMoto levels 5 | 6 | Set mass to 0 for static objects. 7 | 8 | ![ScreenShot](http://chozabu.net/sheepmachine.gif) 9 | ![ScreenShot](http://chozabu.net/kiventss.png) 10 | 11 | Building: 12 | first you must install KivEnt - at the time of writing you must use my gl_polygen branch. 13 | here https://github.com/chozabu/KivEnt/tree/kivented 14 | 15 | polygon2 is required 16 | sudo pip install polygon 17 | 18 | 19 | if you wish to compile for android probably should install libatlas3-base (on ubuntu) as the default has processor-specific instructions. 20 | 21 | Also - there should be a recent (probably working) build here: http://chozabu.net/KivEntEdLatest.apk 22 | -------------------------------------------------------------------------------- /scripts/collision/pushSecondWithFirst.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | import cymunk as cy 4 | from math import * 5 | 6 | #this is a refrence to kiventeds scripting system 7 | scripty = None 8 | 9 | defaults = { 10 | "pushforce": 30, 11 | } 12 | 13 | def collision_func(space, arbiter): 14 | first_body = arbiter.shapes[0].body 15 | second_body = arbiter.shapes[1].body 16 | 17 | 18 | e1 = scripty.gameref.getEntFromID(first_body.data) 19 | if hasattr(e1, 'datadict'): 20 | dd = e1.datadict 21 | impulse = cy.Vec2d(cos(first_body.angle),sin(first_body.angle)) 22 | forcemul = 0 23 | if 'pushforce' in dd: 24 | forcemul=float(dd['pushforce']) 25 | else: 26 | forcemul=float(defaults['pushforce']) 27 | impulse.x*=forcemul 28 | impulse.y*=forcemul 29 | second_body.apply_impulse(impulse) 30 | return False -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | #from http://stackoverflow.com/a/13276237/445831 - Sasha Chedygov 4 | class TwoWayDict(dict): 5 | #def __init__(self,iterable=None, **kwargs): 6 | # super(TwoWayDict,self).__init__(iterable, **kwargs) 7 | 8 | def __setitem__(self, key, value): 9 | # Remove any previous connections with these values 10 | if key in self: 11 | del self[key] 12 | if value in self: 13 | del self[value] 14 | dict.__setitem__(self, key, value) 15 | dict.__setitem__(self, value, key) 16 | 17 | def __delitem__(self, key): 18 | dict.__delitem__(self, self[key]) 19 | dict.__delitem__(self, key) 20 | 21 | def __len__(self): 22 | """Returns the number of connections""" 23 | # The int() call is for Python 3 24 | return int(dict.__len__(self) / 2) -------------------------------------------------------------------------------- /scripts/collision/changeGravAngle.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | import cymunk as cy 4 | from math import * 5 | 6 | #this is a refrence to kiventeds scripting system 7 | scripty = None 8 | 9 | #defaults = { 10 | # "pushforce": 30, 11 | #} 12 | 13 | def collision_func(space, arbiter): 14 | first_body = arbiter.shapes[0].body 15 | second_body = arbiter.shapes[1].body 16 | 17 | 18 | e1 = scripty.gameref.getEntFromID(first_body.data) 19 | dd = e1.datadict 20 | impulse = cy.Vec2d(cos(first_body.angle),sin(first_body.angle)) 21 | #forcemul = 0 22 | #if 'pushforce' in dd: 23 | # forcemul=float(dd['pushforce']) 24 | #else: 25 | # forcemul=float(defaults['pushforce']) 26 | g = scripty.gameref.space.gravity 27 | gpower = sqrt(g.x**2 + g.y**2) 28 | impulse.x*=gpower 29 | impulse.y*=gpower 30 | scripty.gameref.space.gravity = impulse 31 | #print impulse 32 | #second_body.apply_impulse(impulse) 33 | return False -------------------------------------------------------------------------------- /scripts/collision/physicsZone.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | import cymunk as cy 3 | from math import * 4 | 5 | #this is a refrence to kiventeds scripting system 6 | scripty = None 7 | 8 | defaults = { 9 | "xvelmul": 1, 10 | "yvelmul": 1, 11 | "xforce": 0, 12 | "yforce": 0, 13 | } 14 | 15 | def collision_func(space, arbiter): 16 | first_body = arbiter.shapes[0].body 17 | second_body = arbiter.shapes[1].body 18 | 19 | 20 | e1 = scripty.gameref.getEntFromID(first_body.data) 21 | if hasattr(e1, 'datadict'): 22 | dd = e1.datadict 23 | vel = second_body.velocity 24 | if 'xvelmul' in dd: 25 | vel.x*=float(dd['xvelmul']) 26 | if 'yvelmul' in dd: 27 | vel.y*=float(dd['yvelmul']) 28 | second_body.velocity = vel 29 | impulse = cy.Vec2d(0,0) 30 | if 'xforce' in dd: 31 | impulse.x=float(dd['xforce']) 32 | if 'yforce' in dd: 33 | impulse.y=float(dd['yforce']) 34 | second_body.apply_impulse(impulse) 35 | return False -------------------------------------------------------------------------------- /assets/myatlas.atlas: -------------------------------------------------------------------------------- 1 | {"myatlas-0.png": {"face_ball": [374, 766, 64, 64], "cliffs2": [192, 618, 128, 128], "square": [904, 393, 120, 120], "firefox": [506, 766, 64, 64], "Jetpack": [2, 515, 188, 231], "grassyrock": [712, 618, 128, 128], "Boulder3": [104, 278, 100, 105], "snow": [262, 385, 128, 128], "icecube": [2, 748, 304, 274], "sheep": [132, 385, 128, 128], "face_box": [440, 766, 64, 64], "Dirt": [452, 618, 128, 128], "Grass1": [582, 618, 128, 128], "ice": [842, 618, 128, 128], "emptybox": [782, 393, 120, 120], "wood": [652, 385, 128, 128], "windows1": [392, 385, 128, 128], "checksphere": [308, 766, 64, 64], "circle": [869, 902, 120, 120], "wrecker": [572, 766, 64, 64], "Booster": [308, 832, 231, 190], "awheel": [972, 704, 42, 42], "Boulder2": [2, 278, 100, 105], "orb": [2, 385, 128, 128], "plank": [541, 843, 128, 13], "Wood3": [522, 385, 128, 128], "dirtclod": [991, 990, 32, 32], "concreteplates": [322, 618, 128, 128], "arrow": [707, 902, 160, 120], "magicball": [541, 858, 164, 164]}} -------------------------------------------------------------------------------- /serialisation.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | '''This class loads save/load import/export plugins until they are loaded properly as plugins''' 4 | 5 | import scripts.serials.KEE as jsonserials 6 | import scripts.serials.wheelz as wheelzserials 7 | 8 | 9 | 10 | class Serials(): 11 | def __init__(self, gameref): 12 | self.gameref = gameref 13 | 14 | #create import/exporters #TODO load dynamically 15 | self.jsonserials = jsonserials.Serials(gameref) 16 | self.wheelzserials = wheelzserials.Serials(gameref) 17 | 18 | #wrap methods from seperated classes for now 19 | self.exportDict = self.jsonserials.exportDict 20 | self.loadFromDict = self.jsonserials.loadFromDict 21 | self.loadJSON = self.jsonserials.loadJSON 22 | self.loadExtJSON = self.jsonserials.loadExtJSON 23 | self.exportJSON = self.jsonserials.exportJSON 24 | self.entToDict = self.jsonserials.entToDict 25 | self.loadEntFromDict = self.jsonserials.loadEntFromDict 26 | self.exportXML = self.wheelzserials.exportXML 27 | -------------------------------------------------------------------------------- /assets/shaders/posunicolorshader.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | /* Outputs to the fragment shader */ 7 | varying vec4 frag_color; 8 | 9 | /* vertex attributes */ 10 | attribute float v0; 11 | attribute float v1; 12 | attribute float v2; 13 | attribute float v3; 14 | attribute float v4; 15 | attribute float v5; 16 | attribute float v6; 17 | attribute float v7; 18 | attribute vec4 vColor; 19 | 20 | 21 | /* uniform variables */ 22 | uniform mat4 modelview_mat; 23 | uniform mat4 projection_mat; 24 | uniform vec4 color; 25 | uniform float opacity; 26 | 27 | void main (void) { 28 | frag_color = vColor; 29 | vec4 pos = vec4(v0, v1, 0.0, 1.0); 30 | gl_Position = projection_mat * pos; 31 | 32 | } 33 | 34 | 35 | ---FRAGMENT SHADER--- 36 | #ifdef GL_ES 37 | precision highp float; 38 | #endif 39 | 40 | /* Outputs from the vertex shader */ 41 | varying vec4 frag_color; 42 | 43 | void main (void){ 44 | gl_FragColor = frag_color; 45 | } 46 | -------------------------------------------------------------------------------- /scripts/collision/grav2first.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | import cymunk as cy 3 | from math import * 4 | 5 | #this is a refrence to kiventeds scripting system 6 | scripty = None 7 | 8 | 9 | defaults = { 10 | "forcemul": 1 11 | } 12 | 13 | def collision_func(space, arbiter): 14 | firstshape = arbiter.shapes[0] 15 | if firstshape.__class__.__name__ != "Circle": return True 16 | first_body = firstshape.body 17 | second_body = arbiter.shapes[1].body 18 | first_pos = first_body.position 19 | second_pos = second_body.position 20 | diff = cy.Vec2d(first_pos.x-second_pos.x,first_pos.y-second_pos.y) 21 | dist = sqrt(diff.x**2+diff.y**2) 22 | uv = cy.Vec2d(diff.x/dist, diff.y/dist) 23 | invrad = firstshape.radius-dist 24 | if invrad <=0001: invrad = 0001 25 | invrad = sqrt(invrad)*second_body.mass 26 | 27 | e1 = scripty.gameref.getEntFromID(first_body.data) 28 | if hasattr(e1, 'datadict'): 29 | if 'forcemul' in e1.datadict: 30 | invrad*=float(e1.datadict['forcemul']) 31 | force = cy.Vec2d(uv.x*invrad, uv.y*invrad) 32 | second_body.apply_impulse(force) 33 | return False -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | kivent dynamic polygon support 2 | -multi-shape physics UI 3 | --control/view settings of individual shapes in body. 4 | -merging/splitting polys 5 | -updating poly points 6 | --individual point edit/add/delete 7 | --smoothe selection 8 | -generate optimal convex hulls from concave 9 | --current triangle winding check could be better 10 | 11 | -multiple selection 12 | --select box, individual add/subtract 13 | --edit friction, mass, width, etc 14 | 15 | tag items on server 16 | 17 | -upload/download ents/atlas/scripts to net 18 | --save/load external entitys 19 | --save/load alternate atlas 20 | 21 | -edit non-physics objects 22 | 23 | -edit joints 24 | --select joints 25 | --delete joints 26 | --change joint params 27 | 28 | improve UI 29 | -modularise 30 | -images 31 | -scroll tables, rather than box views? 32 | 33 | more scripting? 34 | -set on update scripts? 35 | -keyboard/touch 36 | -tools as scripts? 37 | -script UI? 38 | 39 | 40 | -import wheelz levels? 41 | -improve wheelz export 42 | --export joints better 43 | 44 | 45 | Games: 46 | -extreme tilt maze 47 | -MyWheelz 48 | 49 | Kivent: 50 | -texture importing and/or themes 51 | --material library probably best option 52 | -------------------------------------------------------------------------------- /assets/shaders/poscolorshader.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | /* Outputs to the fragment shader */ 7 | varying vec4 frag_color; 8 | varying vec2 tex_coord0; 9 | 10 | /* vertex attributes */ 11 | attribute float v0; 12 | attribute float v1; 13 | attribute float v2; 14 | attribute float v3; 15 | attribute float v4; 16 | attribute float v5; 17 | attribute float v6; 18 | attribute float v7; 19 | //attribute vec2 vTexCoords0; 20 | 21 | 22 | /* uniform variables */ 23 | uniform mat4 modelview_mat; 24 | uniform mat4 projection_mat; 25 | uniform vec4 color; 26 | uniform float opacity; 27 | 28 | void main (void) { 29 | frag_color = vec4(v2, v3, v4, v5); 30 | tex_coord0 = vec2(v6, v7); 31 | vec4 pos = vec4(v0, v1, 0.0, 1.0); 32 | gl_Position = projection_mat * pos; 33 | 34 | } 35 | 36 | 37 | ---FRAGMENT SHADER--- 38 | #ifdef GL_ES 39 | precision highp float; 40 | #endif 41 | 42 | /* Outputs from the vertex shader */ 43 | varying vec4 frag_color; 44 | varying vec2 tex_coord0; 45 | 46 | /* uniform texture samplers */ 47 | uniform sampler2D texture0; 48 | 49 | void main (void){ 50 | vec2 tc=vec2(fract(tex_coord0.s),fract(tex_coord0.t)); 51 | gl_FragColor = frag_color * texture2D(texture0, tc); 52 | } 53 | -------------------------------------------------------------------------------- /assets/glsl/positionshader.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | /* Outputs to the fragment shader */ 7 | varying vec4 frag_color; 8 | varying vec2 tex_coord0; 9 | 10 | /* vertex attributes */ 11 | attribute vec2 vPosition; 12 | attribute vec2 vTexCoords0; 13 | attribute vec2 vCenter; 14 | 15 | /* uniform variables */ 16 | uniform mat4 modelview_mat; 17 | uniform mat4 projection_mat; 18 | uniform vec4 color; 19 | uniform float opacity; 20 | 21 | void main (void) { 22 | frag_color = color * vec4(1.0, 1.0, 1.0, opacity); 23 | tex_coord0 = vTexCoords0; 24 | mat4 trans_mat = mat4(1.0, 0.0, 0.0, vCenter.x, 25 | 0.0, 1.0, 0.0, vCenter.y, 26 | 0.0, 0.0, 1.0, 0.0, 27 | 0.0, 0.0, 0.0, 1.0); 28 | vec4 pos = vec4(vPosition.xy*.5, 0.0, 1.0); 29 | vec4 trans_pos = pos * trans_mat; 30 | gl_Position = projection_mat * modelview_mat * trans_pos; 31 | 32 | } 33 | 34 | 35 | ---FRAGMENT SHADER--- 36 | #ifdef GL_ES 37 | precision highp float; 38 | #endif 39 | 40 | /* Outputs from the vertex shader */ 41 | varying vec4 frag_color; 42 | varying vec2 tex_coord0; 43 | 44 | /* uniform texture samplers */ 45 | uniform sampler2D texture0; 46 | 47 | void main (void){ 48 | gl_FragColor = frag_color * texture2D(texture0, tex_coord0); 49 | } 50 | -------------------------------------------------------------------------------- /assets/glsl/positionrotateshader.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | /* Outputs to the fragment shader */ 7 | varying vec4 frag_color; 8 | varying vec2 tex_coord0; 9 | 10 | /* vertex attributes */ 11 | attribute vec2 vPosition; 12 | attribute vec2 vTexCoords0; 13 | attribute vec2 vCenter; 14 | attribute vec4 vColor; 15 | attribute float vRotation; 16 | 17 | /* uniform variables */ 18 | uniform mat4 modelview_mat; 19 | uniform mat4 projection_mat; 20 | uniform vec4 color; 21 | uniform float opacity; 22 | 23 | void main (void) { 24 | frag_color = color * vec4(1.0, 1.0, 1.0, opacity)* vColor; 25 | tex_coord0 = vTexCoords0; 26 | float a_sin = sin(vRotation); 27 | float a_cos = cos(vRotation); 28 | mat4 rot_mat = mat4(a_cos, -a_sin, 0.0, 0.0, 29 | a_sin, a_cos, 0.0, 0.0, 30 | 0.0, 0.0, 1.0, 0.0, 31 | 0.0, 0.0, 0.0, 1.0 ); 32 | mat4 trans_mat = mat4(1.0, 0.0, 0.0, vCenter.x, 33 | 0.0, 1.0, 0.0, vCenter.y, 34 | 0.0, 0.0, 1.0, 0.0, 35 | 0.0, 0.0, 0.0, 1.0); 36 | vec4 pos = vec4(vPosition.xy*.5, 0.0, 1.0); 37 | vec4 trans_pos = pos * rot_mat * trans_mat; 38 | gl_Position = projection_mat * modelview_mat * trans_pos; 39 | 40 | } 41 | 42 | 43 | ---FRAGMENT SHADER--- 44 | #ifdef GL_ES 45 | precision highp float; 46 | #endif 47 | 48 | /* Outputs from the vertex shader */ 49 | varying vec4 frag_color; 50 | varying vec2 tex_coord0; 51 | 52 | /* uniform texture samplers */ 53 | uniform sampler2D texture0; 54 | 55 | void main (void){ 56 | gl_FragColor = frag_color * texture2D(texture0, tex_coord0); 57 | } 58 | -------------------------------------------------------------------------------- /assets/glsl/positionrotateshadernocol.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | /* Outputs to the fragment shader */ 7 | varying vec4 frag_color; 8 | varying vec2 tex_coord0; 9 | 10 | /* vertex attributes */ 11 | attribute vec2 vPosition; 12 | attribute vec2 vTexCoords0; 13 | attribute vec2 vCenter; 14 | attribute vec4 vColor; 15 | attribute float vRotation; 16 | 17 | /* uniform variables */ 18 | uniform mat4 modelview_mat; 19 | uniform mat4 projection_mat; 20 | uniform vec4 color; 21 | uniform float opacity; 22 | 23 | void main (void) { 24 | frag_color = color * vec4(1.0, 1.0, 1.0, opacity);//* vColor; 25 | tex_coord0 = vTexCoords0; 26 | float a_sin = sin(vRotation); 27 | float a_cos = cos(vRotation); 28 | mat4 rot_mat = mat4(a_cos, -a_sin, 0.0, 0.0, 29 | a_sin, a_cos, 0.0, 0.0, 30 | 0.0, 0.0, 1.0, 0.0, 31 | 0.0, 0.0, 0.0, 1.0 ); 32 | mat4 trans_mat = mat4(1.0, 0.0, 0.0, vCenter.x, 33 | 0.0, 1.0, 0.0, vCenter.y, 34 | 0.0, 0.0, 1.0, 0.0, 35 | 0.0, 0.0, 0.0, 1.0); 36 | vec4 pos = vec4(vPosition.xy*.5, 0.0, 1.0); 37 | vec4 trans_pos = pos * rot_mat * trans_mat; 38 | gl_Position = projection_mat * modelview_mat * trans_pos; 39 | 40 | } 41 | 42 | 43 | ---FRAGMENT SHADER--- 44 | #ifdef GL_ES 45 | precision highp float; 46 | #endif 47 | 48 | /* Outputs from the vertex shader */ 49 | varying vec4 frag_color; 50 | varying vec2 tex_coord0; 51 | 52 | /* uniform texture samplers */ 53 | uniform sampler2D texture0; 54 | 55 | void main (void){ 56 | gl_FragColor = frag_color * texture2D(texture0, tex_coord0); 57 | } 58 | -------------------------------------------------------------------------------- /assets/shaders/noiseshader.glsl: -------------------------------------------------------------------------------- 1 | ---VERTEX SHADER--- 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | // 7 | // Description : Array and textureless GLSL 2D simplex noise function. 8 | // Author : Ian McEwan, Ashima Arts. 9 | // Maintainer : ijm 10 | // Lastmod : 20110822 (ijm) 11 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 12 | // Distributed under the MIT License. See LICENSE file. 13 | // https://github.com/ashima/webgl-noise 14 | // 15 | 16 | vec3 mod289(vec3 x) { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec2 mod289(vec2 x) { 21 | return x - floor(x * (1.0 / 289.0)) * 289.0; 22 | } 23 | 24 | vec3 permute(vec3 x) { 25 | return mod289(((x*34.0)+1.0)*x); 26 | } 27 | 28 | float snoise(vec2 v) 29 | { 30 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 31 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 32 | -0.577350269189626, // -1.0 + 2.0 * C.x 33 | 0.024390243902439); // 1.0 / 41.0 34 | // First corner 35 | vec2 i = floor(v + dot(v, C.yy) ); 36 | vec2 x0 = v - i + dot(i, C.xx); 37 | 38 | // Other corners 39 | vec2 i1; 40 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 41 | //i1.y = 1.0 - i1.x; 42 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 43 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 44 | // x1 = x0 - i1 + 1.0 * C.xx ; 45 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 46 | vec4 x12 = x0.xyxy + C.xxzz; 47 | x12.xy -= i1; 48 | 49 | // Permutations 50 | i = mod289(i); // Avoid truncation effects in permutation 51 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) 52 | + i.x + vec3(0.0, i1.x, 1.0 )); 53 | 54 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 55 | m = m*m ; 56 | m = m*m ; 57 | 58 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 59 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 60 | 61 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 62 | vec3 h = abs(x) - 0.5; 63 | vec3 ox = floor(x + 0.5); 64 | vec3 a0 = x - ox; 65 | 66 | // Normalise gradients implicitly by scaling m 67 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 68 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 69 | 70 | // Compute final noise value at P 71 | vec3 g; 72 | g.x = a0.x * x0.x + h.x * x0.y; 73 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 74 | return 130.0 * dot(m, g); 75 | } 76 | 77 | 78 | /* Outputs to the fragment shader */ 79 | varying vec4 frag_color; 80 | 81 | /* vertex attributes */ 82 | attribute float v0; 83 | attribute float v1; 84 | attribute float v2; 85 | attribute float v3; 86 | attribute float v4; 87 | attribute float v5; 88 | attribute float v6; 89 | attribute float v7; 90 | 91 | /* uniform variables */ 92 | uniform mat4 modelview_mat; 93 | uniform mat4 projection_mat; 94 | uniform vec4 color; 95 | uniform float opacity; 96 | uniform sampler2D texture0; 97 | 98 | 99 | float sumnoiseoctaves(vec2 p, int octaves, float persistance, float scale) { 100 | float sum = 0.0; 101 | float amp = 1.0; 102 | float freq = scale; 103 | float n = 0.; 104 | int i; 105 | for(i = 0; i < octaves; i+=1) 106 | { 107 | n += snoise(p*freq); 108 | sum += amp; 109 | amp *= persistance; 110 | freq *= 2.; 111 | } 112 | n /= sum; 113 | n = n/2. + .5; 114 | return n; 115 | } 116 | 117 | vec4 getcolor(float n) { 118 | vec4 ret_color = texture2D(texture0, vec2(0., n)); 119 | return ret_color; 120 | } 121 | 122 | void main (void) { 123 | 124 | 125 | frag_color = getcolor(sumnoiseoctaves(vec2(v0, v1), int(v2), v3, v4)); 126 | 127 | gl_Position = projection_mat * modelview_mat * vec4(v0, v1, 0.0, 1.0); 128 | 129 | } 130 | 131 | 132 | ---FRAGMENT SHADER--- 133 | #ifdef GL_ES 134 | precision highp float; 135 | #endif 136 | 137 | /* Outputs from the vertex shader */ 138 | varying vec4 frag_color; 139 | 140 | void main (void){ 141 | gl_FragColor = frag_color; 142 | } 143 | -------------------------------------------------------------------------------- /objscripts.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | 4 | import cymunk as cy 5 | from util import TwoWayDict 6 | from math import * 7 | import os 8 | import glob 9 | import importlib 10 | 11 | class ObjScripts(): 12 | def __init__(self, gameref): 13 | self.gameref = gameref 14 | self.dataDir = gameref.dataDir 15 | self.gameworld = gameref.gameworld 16 | self.space = gameref.space 17 | 18 | self.add_col_func('None') 19 | 20 | self.colfuncs = {} 21 | self.defaults = {} 22 | 23 | foundmods = [ os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__)+"/scripts/collision/*.py")] 24 | foundmods2 = [ os.path.basename(f)[:-4] for f in glob.glob(os.path.dirname(__file__)+"/scripts/collision/*.pyo")] 25 | foundmods = foundmods+foundmods2 26 | #foundmods.remove("__init__") 27 | basepath = "scripts.collision." 28 | for m in foundmods: 29 | #basemod = __import__(basepath+m) 30 | #inmod = getattr(basemod.collision, m) 31 | inmod = __import__(basepath+m, 32 | globals=globals(), 33 | locals=locals(), 34 | fromlist=[m], level=0) 35 | #inmod = importlib.import_module(basepath+m) 36 | if hasattr(inmod, "collision_func"): 37 | inmod.scripty = self 38 | self.colfuncs[m] = inmod.collision_func 39 | self.add_col_func(m) 40 | if hasattr(inmod, "defaults"): 41 | print inmod.defaults 42 | self.defaults[m] = inmod.defaults 43 | 44 | self.collision_types = TwoWayDict() 45 | self.cctype = 0 46 | self.add_col_type('default') 47 | self.add_col_type('vortex') 48 | self.add_col_type('physzone') 49 | self.collision_handlers = { 50 | 'vortex':{ 51 | 'default':{'pre_solve':'grav2first'}, 52 | 'physzone':{'pre_solve':'grav2first'}, 53 | 'vortex':{'pre_solve':'grav2first'} 54 | }, 55 | 'physzone':{ 56 | 'default':{'pre_solve':'physicsZone'} 57 | } 58 | } 59 | self.loadHandlersFromDict(self.collision_handlers) 60 | def getHandlersForType(self, ctype): 61 | ctype = self.collision_types[ctype] 62 | results = [] 63 | print "getting results for", ctype 64 | if ctype in self.collision_handlers: 65 | ch = self.collision_handlers[ctype] 66 | print "ch", ch 67 | for i in ch.values(): 68 | print "i", i 69 | for j in i.values(): 70 | print "j", j 71 | if j not in results: 72 | results.append(j) 73 | return results 74 | def loadHandlersFromDict(self, handlers): 75 | for typeastr, ch in handlers.iteritems(): 76 | #typea = self.collision_types[typeastr] 77 | for typebstr, funcsargs in ch.iteritems(): 78 | #typeb = self.collision_types[typebstr] 79 | #print "funcargs=",funcsargs 80 | self.set_col_handlers(typeastr,typebstr, 81 | **funcsargs) 82 | def add_col_func(self, funcstr): 83 | if funcstr in self.gameref.mainTools.col_funcs: return 84 | self.gameref.mainTools.col_funcs.append(funcstr) 85 | def add_col_type(self,namestr): 86 | if namestr in self.collision_types: return self.collision_types[namestr] 87 | self.collision_types[namestr] = self.cctype 88 | self.gameref.mainTools.col_types.append(namestr) 89 | self.cctype+=1 90 | return self.collision_types[namestr] 91 | def set_col_handlers(self,typeastr,typebstr,begin=None, pre_solve=None, post_solve=None, separate=None): 92 | typea = self.collision_types[typeastr] 93 | typeb = self.collision_types[typebstr] 94 | beginF = self.getCBFunc(begin) 95 | pre_solveF = self.getCBFunc(pre_solve) 96 | post_solveF = self.getCBFunc(post_solve) 97 | separateF = self.getCBFunc(separate) 98 | #print "typea, typeb, func", typea, typeb 99 | self.space.add_collision_handler(typea, typeb, begin=beginF, pre_solve=pre_solveF, post_solve=post_solveF, separate=separateF) 100 | if typeastr not in self.collision_handlers: 101 | self.collision_handlers[typeastr] = {} 102 | otherTypes = self.collision_handlers[typeastr] 103 | if typebstr not in otherTypes: 104 | otherTypes[typebstr] = {} 105 | callers = otherTypes[typebstr] 106 | callers.clear() 107 | if begin: callers['begin'] = begin 108 | if pre_solve: callers['pre_solve'] = pre_solve 109 | if post_solve: callers['post_solve'] = post_solve 110 | if separate: callers['separate'] = separate 111 | 112 | def getCBFunc(self, fname): 113 | if fname == None:return None 114 | if hasattr(self, fname): 115 | return getattr(self, fname) 116 | if fname in self.colfuncs: 117 | return self.colfuncs[fname] 118 | return None -------------------------------------------------------------------------------- /Spline.py: -------------------------------------------------------------------------------- 1 | #This file implements a KB-Spline implementation inspired by public-domain code presented here: 2 | # http://www.gamedev.net/topic/493088-3d-waypoints/ 3 | #The core curve generation code by Ian Mallett is unchanged - check out his page at http://geometrian.com/ 4 | __author__ = 'chozabu' 5 | 6 | class Spline(): 7 | 8 | def __init__(self, stepsize = 1./4., c=0, b=0, t=0): 9 | self.c = c 10 | self.b = b 11 | self.t = t 12 | self.ControlPoints = [] 13 | self.subpoints = [] 14 | self.stepsize = stepsize 15 | self.selected_point = None 16 | def add_or_select(self, mpos, selection_dist, max_range=300): 17 | nearest, dist2, nindex = self.nearestPoint((mpos[0],mpos[1])) 18 | #print "\n" 19 | selected = None 20 | if dist2 < selection_dist**2: 21 | selected = nindex 22 | elif len(self.ControlPoints) < 3 or dist2< max_range**2: 23 | import math 24 | print math.sqrt(dist2) 25 | selected = self.add_point((mpos[0],mpos[1])) 26 | self.selected_point = selected 27 | #print selected 28 | return selected 29 | def remove_point(self, mpos, selection_dist): 30 | nearest, dist2, nindex = self.nearestPoint((mpos[0],mpos[1])) 31 | #print "\n" 32 | if dist2 < selection_dist**2: 33 | self.ControlPoints.remove(nearest) 34 | def add_point(self, mpos): 35 | if len(self.ControlPoints) < 3: 36 | self.ControlPoints.append(mpos) 37 | return len(self.ControlPoints)-1 38 | snearest, sdist2, snindex = self.nearestSubPoint(mpos) 39 | nsindex = snindex*self.stepsize 40 | nindex = int(nsindex+0.5) 41 | #print nsindex, nindex 42 | test = nsindex- nindex 43 | #print test 44 | if test>0 and test < 2: 45 | nindex+=1 46 | self.ControlPoints.insert(nindex, (mpos[0],mpos[1])) 47 | return nindex 48 | 49 | def nearestPoint(self, pos): 50 | return self._nearest(pos, self.ControlPoints) 51 | 52 | def nearestSubPoint(self, pos): 53 | return self._nearest(pos, self.subpoints) 54 | def _nearest(self, pos, points): 55 | shortest = 999999**2 56 | nearest = None 57 | index = -1 58 | retindex = 0 59 | for p in points: 60 | index+=1 61 | xd = p[0]-pos[0] 62 | yd = p[1]-pos[1] 63 | td = xd*xd+yd*yd 64 | if td 2 or iscw2 < -2): 46 | poly_dict = { 47 | 'vertices':pts, 'offset': (0, 0), 'mass':submass} 48 | col_shape = {'shape_type': 'poly', 'elasticity': elasticity, 49 | 'collision_type': collision_type, 'shape_info': poly_dict, 'friction': friction} 50 | col_shapes.append(col_shape) 51 | else: 52 | remlist.append(tindex) 53 | return col_shapes, remlist 54 | 55 | def tristripToKDict(ts, color): 56 | tri_verts = [] 57 | new_triangles = [] 58 | tri_ap = new_triangles.append 59 | vindex = 0 60 | tri_count = 0 61 | for strip in ts: 62 | sindex = 0 63 | striplen = len(strip)-2 64 | for vert in strip: 65 | #print sindex, striplen 66 | if sindex < striplen: 67 | tri_ap((vindex,vindex+1,vindex+2)) 68 | tri_count += 1 69 | tri_verts.append(vert) 70 | vindex+=1 71 | sindex+=1 72 | 73 | new_vertices = [] 74 | nv_ap = new_vertices.append 75 | vert_count = 0 76 | for tvert in tri_verts: 77 | nv_ap([tvert[0], tvert[1], color[0], color[1], color[2], color[3], tvert[0]*0.01, tvert[1]*0.01]) 78 | vert_count += 1 79 | return new_triangles, new_vertices, tri_count, vert_count 80 | 81 | class PolyGen(): 82 | def __init__(self, poly=None, color=(1,1,1,0.9), keepsimple = False, minlinelen=2): 83 | self.color = color 84 | self.poly = poly 85 | self.keepsimple = keepsimple 86 | po.setTolerance(0.1) 87 | self.minlinelen = minlinelen 88 | 89 | def from_spline(self, points): 90 | newp = Polygon() 91 | #print points 92 | newp.addContour(points) 93 | self.poly = newp 94 | 95 | def sub_circle_polygon(self, pos, sides=None, radius=30): 96 | if sides is None:sides = int(8+math.sqrt(radius)) 97 | p1 = Circle(radius, pos, sides)# - Circle(0.5) 98 | if self.poly == None: 99 | print "poly never created!" 100 | return 101 | check = self.poly-p1 102 | if (self.keepsimple and len(check)<2) or not self.keepsimple: 103 | self.poly = check 104 | def draw_circle_polygon(self, pos, sides=None, radius=30): 105 | if sides is None:sides = int(8+math.sqrt(radius)) 106 | p1 = Circle(radius, pos, sides) 107 | 108 | if self.poly == None: 109 | self.poly = p1 110 | return 111 | check = self.poly+p1 112 | if (self.keepsimple and len(check)<2) or not self.keepsimple: 113 | self.poly = check 114 | def draw_square_polygon(self, pos, width,height, angle=0): 115 | p1 = Rectangle(width,height) 116 | p1.shift(-width/2,-height/2) 117 | p1.rotate(angle) 118 | p1.shift(pos[0],pos[1]) 119 | if self.poly == None: 120 | self.poly = p1 121 | return 122 | check = self.poly+p1 123 | if (self.keepsimple and len(check)<2) or not self.keepsimple: 124 | self.poly = check 125 | def sub_square_polygon(self, pos, width,height, angle=0): 126 | p1 = Rectangle(width,height) 127 | p1.shift(-width/2,-height/2) 128 | p1.rotate(angle) 129 | p1.shift(pos[0],pos[1]) 130 | if self.poly == None: 131 | self.poly = p1 132 | return 133 | check = self.poly-p1 134 | if (self.keepsimple and len(check)<2) or not self.keepsimple: 135 | self.poly = check 136 | 137 | def draw_from_Polygon(self): 138 | if len(self.poly) == 0: return False 139 | self.remove_short_lines(self.minlinelen) 140 | pts = self.poly[0] 141 | #writeSVG('Operations.svg', [self.poly], width=800) 142 | new_triangles, new_vertices, tri_count, vert_count =self.pts_to_tristrip(pts) 143 | return {'triangles': new_triangles, 'vertices': new_vertices, 144 | 'vert_count': vert_count, 'tri_count': tri_count, 145 | 'vert_data_count': 5} 146 | def remove_short_lines(self, minlen = 1): 147 | minlen*=minlen 148 | newp = Polygon() 149 | c=-1 150 | for cont in self.poly: 151 | c+=1 152 | #cont=self.poly[0] 153 | keeplist = [] 154 | lastp = None 155 | pindex = -1 156 | for p in cont: 157 | pindex+=1 158 | if lastp: 159 | #check here 160 | xd = p[0]-lastp[0] 161 | yd = p[0]-lastp[0] 162 | td = xd*xd+yd*yd 163 | if td < minlen: 164 | pass 165 | #lastp = None 166 | else: 167 | keeplist.append(p) 168 | lastp = p 169 | else: 170 | keeplist.append(p) 171 | lastp = p 172 | ishole = self.poly.isHole(c) 173 | newp.addContour(keeplist,ishole) 174 | self.poly = newp 175 | def remove_some_pts(self, prop=.9): 176 | newp = Polygon() 177 | c=-1 178 | for cont in self.poly: 179 | c+=1 180 | cr = reducePoints(cont,int(len(cont)*prop)) 181 | ishole = self.poly.isHole(c) 182 | if len(cr)>2: 183 | newp.addContour(cr, ishole) 184 | parea = self.poly.area() 185 | nparea = newp.area() 186 | #areadiff = math.fabs(nparea-parea) 187 | arearatio = parea/nparea 188 | arearatio = math.fabs(arearatio-1) 189 | #print areadiff, arearatio 190 | if arearatio < 0.1: 191 | self.poly = newp 192 | def pts_to_tristrip(self, pts): 193 | ts = self.poly.triStrip() 194 | color = self.color 195 | return tristripToKDict(ts,color) 196 | 197 | '''def initentity(self): 198 | #create_dict = self.draw_rect_polygon( 199 | # 20, 100, (150, 150), 3, .5, .005, '150') 200 | create_dict = self.draw_from_Polygon() 201 | #create_dict['do_texture'] = True 202 | #create_dict['texture'] = 'assets/planetgradient2.png' 203 | a = self.gameworld.init_entity({'noise_renderer2': create_dict}, 204 | ['noise_renderer2'])''' -------------------------------------------------------------------------------- /examples/pong.json: -------------------------------------------------------------------------------- 1 | {"jointslist": [], "ents": [{"physics_renderer": {"width": 190.0, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 35, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 540.0, "x": 288.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 190.0}], "shape_type": "box", "body": {"position": [288.0, 540.0], "angle": 0.0, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 0.0}}, {"physics_renderer": {"width": 464.0, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 36, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 307.0, "x": 193.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 464.0}], "shape_type": "box", "body": {"position": [193.0, 307.0], "angle": 1.5700000524520874, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 1.5700000524520874}}, {"physics_renderer": {"width": 190.0, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 37, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 76.0, "x": 288.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 190.0}], "shape_type": "box", "body": {"position": [288.0, 76.0], "angle": 3.140000104904175, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 3.140000104904175}}, {"physics_renderer": {"width": 185.00270080566406, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 38, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 76.5, "x": 553.5}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 185.00270080566406}], "shape_type": "box", "body": {"position": [553.5, 76.5], "angle": 3.1361873149871826, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 3.1361873149871826}}, {"physics_renderer": {"width": 464.00970458984375, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 39, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 310.0, "x": 644.5}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 464.00970458984375}], "shape_type": "box", "body": {"position": [644.5, 310.0], "angle": -1.559999942779541, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": -1.559999942779541}}, {"physics_renderer": {"width": 171.0029296875, "texture": "plank", "height": 10.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 40, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 542.5, "x": 557.5}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 10.0, "width": 171.0029296875}], "shape_type": "box", "body": {"position": [557.5, 542.5], "angle": -0.009999999776482582, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": -0.009999999776482582}}, {"physics_renderer": {"width": 17.20465087890625, "texture": "sheep", "height": 17.20465087890625}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 41, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 420.53265380859375, "x": 476.12298583984375}, "physics": {"shapes": [{"collision_type": "default", "radius": 8.602325439453125, "group": 0, "elasticity": 2.0, "friction": 1.0}], "shape_type": "circle", "body": {"position": [476.12298583984375, 420.53265380859375], "angle": 198.04585412003476, "vel_limit": 2048.0, "mass": 10.0, "ang_vel_limit": 34.906585693359375, "velocity": [126.18000030517578, 92.55999755859375], "angular_velocity": 11.750880378292782}}, "rotate": {"r": 198.0458526611328}}, {"physics_renderer": {"width": 77.0, "texture": "face_box", "height": 18.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 42, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 107.0, "x": 422.5}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 18.0, "width": 77.0}], "shape_type": "box", "body": {"position": [422.5, 107.0], "angle": 0.0, "vel_limit": 2048.0, "mass": 10.0, "ang_vel_limit": 34.906585693359375, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 0.0}}, {"physics_renderer": {"width": 90.0, "texture": "face_box", "height": 21.0}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 51, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 511.5, "x": 428.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 21.0, "width": 90.0}], "shape_type": "box", "body": {"position": [428.0, 511.5], "angle": 0.0, "vel_limit": 2048.0, "mass": 10.0, "ang_vel_limit": 34.906585693359375, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": 0.0}}, {"physics_renderer": {"width": 65.11528015136719, "texture": "grassyrock", "height": 65.11528015136719}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 52, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 330.0, "x": 191.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 65.11528015136719, "width": 65.11528015136719}], "shape_type": "box", "body": {"position": [191.0, 330.0], "angle": -2.4000000953674316, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": -2.4000000953674316}}, {"physics_renderer": {"width": 60.8276252746582, "texture": "grassyrock", "height": 60.8276252746582}, "color": [1.0, 1.0, 1.0, 1.0], "orig_id": 50, "load_order": ["color", "position", "rotate", "physics", "physics_renderer"], "position": {"y": 327.0, "x": 640.0}, "physics": {"shapes": [{"collision_type": "default", "group": 0, "elasticity": 0.5, "friction": 1.0, "height": 60.8276252746582, "width": 60.8276252746582}], "shape_type": "box", "body": {"position": [640.0, 327.0], "angle": -0.7599999904632568, "vel_limit": Infinity, "mass": Infinity, "ang_vel_limit": Infinity, "velocity": [0.0, 0.0], "angular_velocity": 0.0}}, "rotate": {"r": -0.7599999904632568}}], "collision_typesdict": {"vortex": {"default": {"pre_solve": "grav2first"}, "physzone": {"pre_solve": "grav2first"}, "vortex": {"pre_solve": "grav2first"}}, "physzone": {"default": {"pre_solve": "physicsZone"}}}, "settings": {"startID": -1, "gravity": [0.0, 0.0], "finishID": -1}, "collision_typeslist": ["default", "vortex", "physzone"]} -------------------------------------------------------------------------------- /scripts/serials/wheelz.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | 4 | import xml.etree.ElementTree as ET 5 | from math import cos, sin 6 | import math 7 | 8 | xmTexDict = { 9 | "Grass1":"default", 10 | "Dirt":"Dirt", 11 | "snow":"snow", 12 | "wood":"wood", 13 | "windows1":"windows1", 14 | "Wood3":"Wood3", 15 | 16 | "face_ball":"default", 17 | "firefox": "default", 18 | "cliffs2": "DarkDirt", 19 | "square": "default", 20 | "grassyrock": "DarkDirt", 21 | "sheep": "ball", 22 | "ice": "ice2", 23 | "start": "default", 24 | "emptybox": "default", 25 | "checksphere": "default", 26 | "circle": "default", 27 | "wrecker": "default", 28 | "awheel": "default", 29 | "orb": "default", 30 | "plank": "Wood2", 31 | "dirtclod": "default", 32 | "concreteplates": "Asphalt1", 33 | "arrow": "default", 34 | "magicball": "default" 35 | 36 | } 37 | 38 | 39 | class Serials(): 40 | def __init__(self, gameref): 41 | self.gameref = gameref 42 | self.dataDir = gameref.dataDir 43 | self.gameworld = gameref.gameworld 44 | self.space = gameref.space 45 | def getcircleverts(self, radius): 46 | angle = 0 47 | count = 6.0 48 | step = math.pi/count 49 | verts = [] 50 | for i in range(int(count*2)): 51 | angle = step*i 52 | verts.append((cos(angle)*radius,sin(angle)*radius)) 53 | return verts 54 | 55 | #http://stackoverflow.com/questions/22364828/create-a-square-polygon-random-oriented-from-midpoints-in-python 56 | def getboxverts(self, angle, width, height): 57 | """ Calculate coordinates of a rotated square centered at 'cx, cy' 58 | given its 'size' and rotation by 'degrees' about its center. 59 | """ 60 | l, r, b, t = -width/2, width/2,-height/2, height/2 61 | a = angle#radians(degrees) 62 | cosa, sina = cos(-a), sin(-a) 63 | pts = [(l, b), (l, t), (r, t), (r, b)] 64 | return [(( (x)*cosa + (y)*sina), 65 | (-(x)*sina + (y)*cosa)) for x, y in pts] 66 | 67 | 68 | def entToXML(self, e, root, xmScale): 69 | ''' 70 | 71 | 72 | 73 | 74 | ''' 75 | ''' 76 | info = ET.SubElement(root,'info') 77 | info.set('id','levelid') 78 | name = ET.SubElement(info,'name') 79 | name.text="LevelName" 80 | ''' 81 | 82 | if (not hasattr(e, "physics") or not hasattr(e, "physics_renderer")) and not hasattr(e, 'polyshape'): 83 | print "not doing shape" 84 | return None 85 | 86 | if e.entity_id == self.gameref.startID or e.entity_id == self.gameref.finishID: 87 | ed = ET.SubElement(root,'entity') 88 | pd = ET.SubElement(ed,'position') 89 | if e.entity_id == self.gameref.startID: 90 | ed.set('typeid', 'PlayerStart') 91 | if e.entity_id == self.gameref.finishID: 92 | ed.set('typeid', 'EndOfLevel') 93 | pd.set("x", str(e.position.x*xmScale)) 94 | pd.set("y", str(e.position.y*xmScale)) 95 | sd = ET.SubElement(ed,'size') 96 | sd.set("r", "0.5") 97 | return 98 | ed = ET.SubElement(root,'block') 99 | ed.set("id", str(e.entity_id)) 100 | 101 | 102 | 103 | #info = ET.SubElement(root,'info') 104 | #info.set('id','levelid') 105 | #if hasattr(e, "color"): 106 | # ed["color"] = [e.color.r,e.color.g,e.color.b,e.color.a] 107 | shape_type = 'poly' 108 | 109 | # 110 | # 111 | pd = ET.SubElement(ed,'position') 112 | pd.set("x", str(e.position.x*xmScale)) 113 | pd.set("y", str(e.position.y*xmScale)) 114 | #pd.set("background", "false") 115 | 116 | if hasattr(e, "physics"): 117 | pd.set("background", "false") 118 | phd = ET.SubElement(ed,'physics') 119 | phd.set("grip", str(e.physics.shapes[0].friction*20.0)) 120 | phd.set("friction", str(e.physics.shapes[0].friction)) 121 | b = e.physics.body 122 | shape_type = e.physics.shape_type 123 | if not e.physics.body.is_static: 124 | pd.set("physics", "true") 125 | phd.set("mass", str(e.physics.body.mass)) 126 | # 127 | else: 128 | self.laststatic = e.entity_id 129 | else: 130 | pd.set("background", "true") 131 | pr = None 132 | texname = None 133 | if hasattr(e, 'physics_renderer'): 134 | pr = e.physics_renderer 135 | texname = pr.texture 136 | if hasattr(e, 'poly_renderer'): 137 | pr = e.poly_renderer 138 | texname = pr.texture.split('/')[-1][:-4] 139 | if texname: 140 | td = ET.SubElement(ed,'usetexture') 141 | texname = xmTexDict.get(texname,texname) 142 | td.set("id", texname) 143 | verts = [] 144 | if shape_type == "box": 145 | verts = self.getboxverts(b.angle, e.physics_renderer.width, e.physics_renderer.height) 146 | elif shape_type == "circle": 147 | verts = self.getcircleverts(e.physics_renderer.width/2.0) 148 | elif shape_type == "poly": 149 | verts = e.polyshape.poly[0] 150 | for v in verts: 151 | vd = ET.SubElement(ed,'vertex') 152 | vd.set("x", str(v[0]*xmScale)) 153 | vd.set("y", str(v[1]*xmScale)) 154 | 155 | #e.physics_renderer.texture 156 | 157 | def exportEntsToXML(self, root, xmScale): 158 | entsdict = [] 159 | for eid in self.gameref.entIDs: 160 | e = self.gameworld.entities[eid] 161 | self.entToXML(e, root, xmScale) 162 | 163 | ''' 164 | 165 | 166 | 167 | 168 | ''' 169 | def exportJointsToXML(self, root, xmScale): 170 | space = self.space 171 | for j in space.constraints: 172 | jtype = j.__class__.__name__ 173 | anchor1 = j.anchor1 174 | #if None == j.a.data and j.a != space.static_body: 175 | # anchor1 = {'x':j.a.position.x, 'y':j.a.position.y} 176 | 177 | anchor2 = j.anchor2 178 | if anchor2['x'] == 0 and anchor2['y'] == 0 and j.b.data is None: 179 | anchor2 = {'x': j.b.position.x, 'y': j.b.position.y} 180 | print jtype 181 | if jtype == "PivotJoint" or jtype == "PinJoint": 182 | ed = ET.SubElement(root,'entity') 183 | ed.set('id', str(j)) 184 | ed.set('typeid', 'Joint') 185 | 186 | pd = ET.SubElement(ed,'position') 187 | #pd.set("x", str(e.position.x*xmScale)) 188 | #pd.set("y", str(e.position.y*xmScale)) 189 | pd.set("x", str(j.a.position.x*xmScale)) 190 | pd.set("y", str(j.a.position.y*xmScale)) 191 | 192 | sd = ET.SubElement(ed,'size') 193 | sd.set("r", "0.5") 194 | end = j.b.data 195 | if end == None: end=self.laststatic 196 | jd = ET.SubElement(ed,'joint') 197 | jd.set("type", "pivot") 198 | jd.set("connection-end", str(j.a.data)) 199 | jd.set("connection-start", str(end)) 200 | ''' 201 | jd = {"type": jtype, "a": j.a.data, "b": j.b.data, 202 | "anchor1": anchor1, "anchor2": anchor2} 203 | if jtype == "DampedSpring": 204 | jd['rest_length'] = j.rest_length 205 | jd['stiffness'] = j.stiffness 206 | jd['damping'] = j.damping 207 | jds.append(jd)''' 208 | 209 | def exportXML(self, fileName="defaultlevel.lvl", xmScale = 0.05): 210 | root = ET.Element('level') 211 | info = ET.SubElement(root,'info') 212 | info.set('id','levelid') 213 | name = ET.SubElement(info,'name') 214 | name.text="LevelName" 215 | sky = ET.SubElement(info,'sky') 216 | sky.text="sky1" 217 | 218 | limits = ET.SubElement(root,'info') 219 | limits.set('id','levelid') 220 | self.exportEntsToXML(root, xmScale) 221 | self.exportJointsToXML(root, xmScale) 222 | 223 | #info = ET.SubElement(root,'info') 224 | #info.set('id','levelid') 225 | 226 | tree = ET.ElementTree(root) 227 | from kivy.utils import platform 228 | if platform == 'android': 229 | if not os.path.exists('/sdcard/xlvls/'): 230 | os.makedirs('/sdcard/xlvls/') 231 | fileName = '/sdcard/xlvls/'+fileName 232 | tree.write(fileName) 233 | print "saved", fileName -------------------------------------------------------------------------------- /scripts/serials/KEE.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chozabu' 2 | 3 | import json 4 | import cymunk as cy 5 | import os 6 | import Polygon.IO as pio 7 | import PolyGen, Spline 8 | 9 | import base64 10 | 11 | class Serials(): 12 | def __init__(self, gameref): 13 | self.gameref = gameref 14 | self.dataDir = gameref.dataDir 15 | self.gameworld = gameref.gameworld 16 | self.space = gameref.space 17 | 18 | 19 | def shapeToDict(self, shape): 20 | ct = self.gameref.scripty.collision_types 21 | sd = {'collision_type': ct[shape.collision_type], 'elasticity': shape.elasticity, 'friction': shape.friction, 22 | 'group': shape.group} 23 | shapetype = shape.__class__.__name__ 24 | if shapetype == "Circle": 25 | sd['radius'] = shape.radius 26 | elif shapetype == "BoxShape": 27 | sd['width'] = shape.width 28 | sd['height'] = shape.height 29 | elif shapetype == "Poly": 30 | try: 31 | verts = shape.get_local_vertices() 32 | vertups = [(v.x, v.y) for v in verts] 33 | sd['verts'] = vertups 34 | except: 35 | print "verts not exported" 36 | return sd 37 | 38 | def entToDict(self, e): 39 | ed = {"orig_id": e.entity_id} 40 | #'load_order', 'physics', 'physics_renderer', 'position', 'rotate' 41 | if hasattr(e, "load_order"): 42 | ed["load_order"] = e.load_order 43 | if hasattr(e, "color"): 44 | ed["color"] = [e.color.r,e.color.g,e.color.b,e.color.a] 45 | if hasattr(e, 'splineshape'): 46 | ss = e.splineshape 47 | points = list(ss.ControlPoints) 48 | ed["splineshape"] = {'ControlPoints':points, 'stepsize':ss.stepsize} 49 | if hasattr(e, "polyshape"): 50 | poly = e.polyshape.poly 51 | polystr = base64.encodestring(pio.encodeBinary(poly)) 52 | ed["polyviewbinary"] = polystr 53 | ed["polyviewtristrip"] = poly.triStrip() 54 | 55 | postr = [] 56 | index = 0 57 | for line in poly: 58 | postr.append({"ishole":poly.isHole(index), "line":line}) 59 | index+=1 60 | ed["polyview"] = postr 61 | if hasattr(e, "physics"): 62 | b = e.physics.body 63 | bd = {'velocity': (b.velocity.x, b.velocity.y), 64 | 'position': (b.position.x, b.position.y), 65 | 'angle': b.angle, 66 | 'angular_velocity': b.angular_velocity, 67 | 'vel_limit': b.velocity_limit, 68 | 'ang_vel_limit': b.angular_velocity_limit, 69 | 'mass': b.mass 70 | } 71 | shapes = [] 72 | for s in e.physics.shapes: 73 | #print s 74 | #print dir(s) 75 | shapes.append(self.shapeToDict(s)) 76 | pd = {"shapes": shapes, "shape_type": e.physics.shape_type, "body": bd} 77 | ed["physics"] = pd 78 | if hasattr(e, "poly_renderer"): 79 | prd = {"texture": e.poly_renderer.texture} 80 | ed["poly_renderer"] = prd 81 | if hasattr(e, "physics_renderer"): 82 | prd = {"width": e.physics_renderer.width, "height": e.physics_renderer.height, 83 | "texture": e.physics_renderer.texture} 84 | ed["physics_renderer"] = prd 85 | if hasattr(e, "renderer"): 86 | prd = {"width": e.renderer.width, "height": e.renderer.height, 87 | "texture": e.renderer.texture} 88 | ed["renderer"] = prd 89 | if hasattr(e, "position"): 90 | pd = {"x": e.position.x, "y": e.position.y} 91 | ed["position"] = pd 92 | if hasattr(e, "rotate"): 93 | rd = {"r": e.rotate.r} 94 | ed["rotate"] = rd 95 | if hasattr(e, 'datadict'): 96 | ed['datadict'] = dict(e.datadict) 97 | return ed 98 | 99 | def exportJointsToDicts(self): 100 | space = self.space 101 | jds = [] 102 | for j in space.constraints: 103 | jtype = j.__class__.__name__ 104 | anchor1 = j.anchor1 105 | #if None == j.a.data and j.a != space.static_body: 106 | # anchor1 = {'x':j.a.position.x, 'y':j.a.position.y} 107 | 108 | anchor2 = j.anchor2 109 | if anchor2['x'] == 0 and anchor2['y'] == 0 and j.b.data is None: 110 | anchor2 = {'x': j.b.position.x, 'y': j.b.position.y} 111 | jd = {"type": jtype, "a": j.a.data, "b": j.b.data, 112 | "anchor1": anchor1, "anchor2": anchor2} 113 | if jtype == "DampedSpring": 114 | jd['rest_length'] = j.rest_length 115 | jd['stiffness'] = j.stiffness 116 | jd['damping'] = j.damping 117 | jds.append(jd) 118 | return jds 119 | def exportEntsToDicts(self): 120 | entsdict = [] 121 | for eid in self.gameref.entIDs: 122 | e = self.gameworld.entities[eid] 123 | #print "\n" 124 | ed = self.entToDict(e) 125 | entsdict.append(ed) 126 | return entsdict 127 | 128 | def exportDict(self): 129 | space = self.space 130 | entslist = self.exportEntsToDicts() 131 | jointslist = self.exportJointsToDicts() 132 | 133 | collision_typeslist = self.gameref.mainTools.col_types 134 | collision_typesdict = self.gameref.scripty.collision_handlers 135 | 136 | gr = None 137 | gb = self.gameref.mainTools.grav_backup 138 | if self.gameref.mainTools.killMomem and not (gb.x == 0 and gb.y == 0): 139 | gr = gb 140 | else: 141 | gr = self.gameref.space.gravity 142 | gt = (gr.x,gr.y) 143 | 144 | worlddict = {"ents": entslist, "jointslist": jointslist, 145 | "collision_typeslist": collision_typeslist, "collision_typesdict": collision_typesdict, 146 | "settings": {"gravity": gt, 147 | "startID":self.gameref.startID, "finishID": self.gameref.finishID, 148 | "killMomem":self.gameref.mainTools.killMomem, "paused": self.gameref.mainTools.paused}} 149 | return worlddict 150 | 151 | def exportJSON(self, fileName="defaultlevel.json"): 152 | dataDir = self.dataDir 153 | worlddict = self.exportDict() 154 | with open(dataDir + fileName, 'w') as fo: 155 | json.dump(worlddict, fo) 156 | settingsDict = {"lastSave":fileName} 157 | with open(dataDir + "settings.jso", 'w') as fo: 158 | json.dump(settingsDict, fo) 159 | #print "dir=", dataDir 160 | #print "done" 161 | print "saved", fileName 162 | return worlddict 163 | 164 | def loadJSON(self, fileName="defaultlevel.json"): 165 | self.loadExtJSON(self.dataDir + fileName) 166 | 167 | def loadExtJSON(self, fileNamePath): 168 | if not os.path.isfile(fileNamePath): return 169 | with open(fileNamePath) as fo: 170 | entsdict = json.load(fo) 171 | self.loadFromDict(entsdict) 172 | 173 | def loadCollisionTypesFromDict(self, clist, cdata): 174 | scripty = self.gameref.scripty 175 | for ct in clist: 176 | scripty.add_col_type(ct) 177 | scripty.loadHandlersFromDict(cdata) 178 | 179 | 180 | def loadColors(self, s, e): 181 | return tuple(s) 182 | def loadPosition(self, s, e): 183 | return (s['x'],s['y']) 184 | def loadRotate(self, s, e): 185 | return s['r'] 186 | def loadPhysics(self, s, e): 187 | s=dict(s) 188 | #print s 189 | s['col_shapes']=s['shapes'] 190 | for shape in s['col_shapes']: 191 | #print "shape:", shape 192 | if 'radius' in shape: 193 | shape['shape_info']={'inner_radius': 0, 'outer_radius': shape['radius'], 194 | 'mass': s['body']['mass'], 'offset': (0, 0)} 195 | shape['shape_type']='circle' 196 | #print "shape:", shape 197 | elif 'verts' in shape: 198 | shape['shape_info']={'vertices': shape['verts'], 199 | 'mass': s['body']['mass'], 'offset': (0, 0)} 200 | shape['shape_type']='poly' 201 | print shape 202 | elif 'width' in shape: 203 | shape['shape_info']={'width': shape['width'],'height': shape['height'], 204 | 'mass': s['body']['mass']} 205 | shape['shape_type']='box' 206 | else: 207 | print "NOT HANDLING SHAPE", shape 208 | coltypestr = shape['collision_type'] 209 | ct = self.gameref.scripty.collision_types 210 | #print "collision types" 211 | #print shape['collision_type'] 212 | if coltypestr in ct: 213 | collision_type = ct[coltypestr] 214 | if collision_type != str(collision_type): 215 | shape['collision_type'] = collision_type 216 | #print shape['collision_type'] 217 | for x in s['body']: 218 | s[x]=s['body'][x] 219 | if str(s['mass']) == 'inf': s['mass'] = 0 220 | return s 221 | def loadPhysics_renderer(self, s, e): 222 | #{u'width': 60.0, u'texture': u'orb', u'height': 60.0} 223 | #{'texture': texture, 'size': (radius * 2, radius * 2)} 224 | return {'texture': str(s['texture']), 'size': (s['width'],s['height'])} 225 | def loadPoly_renderer(self,s,e): 226 | 227 | new_triangles, new_vertices, tri_count, vert_count =PolyGen.tristripToKDict(e["polyviewtristrip"], self.loadColors(e['color'], None)) 228 | cd = {'triangles': new_triangles, 'vertices': new_vertices, 229 | 'vert_count': vert_count, 'tri_count': tri_count, 230 | 'vert_data_count': 5,'texture': str(s['texture']), 'do_texture':True} 231 | #print cd 232 | #print "polytex:",s['texture'] 233 | return cd 234 | def loadEntFromDict(self, e, idConvDict=None): 235 | sysfuncs={ 236 | 'color':self.loadColors, 237 | 'position':self.loadPosition, 238 | 'rotate':self.loadRotate, 239 | 'physics':self.loadPhysics, 240 | 'physics_renderer':self.loadPhysics_renderer, 241 | 'renderer':self.loadPhysics_renderer, 242 | 'poly_renderer':self.loadPoly_renderer, 243 | } 244 | load_order = [str(li) for li in e['load_order']] 245 | create_dict = {} 246 | for system in load_order: 247 | if system in sysfuncs: 248 | create_dict[str(system)]=sysfuncs[system](e[system],e) 249 | else: 250 | print "unknown system:", system, e[system] 251 | create_dict[str(system)]=e[system] 252 | 253 | entID = self.gameref.create_ent_from_dict(create_dict, load_order, selectNow=False) 254 | if entID != None: 255 | if idConvDict!=None: idConvDict[e['orig_id']] = entID 256 | newent = self.gameref.getEntFromID(entID) 257 | if 'datadict' in e: 258 | newent.datadict = e['datadict'] 259 | if hasattr(newent, 'poly_renderer'): 260 | pg = base64.decodestring(e['polyviewbinary']) 261 | pg = pio.decodeBinary(pg) 262 | pg = PolyGen.PolyGen(pg) 263 | newent.polyshape = pg 264 | if 'splineshape' in e: 265 | print "LOADING SPLINE" 266 | ss = e['splineshape'] 267 | points = ss['ControlPoints'] 268 | newspline = Spline.Spline(stepsize=e['splineshape']['stepsize']) 269 | newspline.ControlPoints = points 270 | newspline.DrawCurve() 271 | newent.splineshape = newspline 272 | return entID 273 | def loadFromDict(self, data): 274 | print "LOADING" 275 | space = self.space 276 | if "collision_typeslist" in data and "collision_typesdict" in data: 277 | self.loadCollisionTypesFromDict(data["collision_typeslist"],data["collision_typesdict"]) 278 | ents = data['ents'] 279 | idConvDict = {} 280 | for e in ents: 281 | self.loadEntFromDict(e, idConvDict=idConvDict) 282 | if "jointslist" in data: 283 | jointslist = data['jointslist'] 284 | for j in jointslist: 285 | if j['a'] in idConvDict: 286 | b1id = idConvDict[j['a']] 287 | #print j['a'], b1id 288 | b1 = self.gameworld.entities[b1id].physics.body 289 | else: 290 | b1 = cy.Body() 291 | if j['b'] in idConvDict: 292 | b2id = idConvDict[j['b']] 293 | #print j['b'], b2id 294 | b2 = self.gameworld.entities[b2id].physics.body 295 | else: 296 | b2 = cy.Body() 297 | kwargs = {} 298 | b1l = j['anchor1'] 299 | b2l = j['anchor2'] 300 | if str(j['type']) == "DampedSpring": 301 | kwargs = {'rest_length':j['rest_length'], 302 | 'stiffness':j['stiffness'], 303 | 'damping':j['damping']} 304 | self.gameref.create_joint(b1, b2, (b1l['x'], b1l['y']), ( 305 | b2l['x'], b2l['y']), str(j['type']), **kwargs) 306 | #space.add(qj) 307 | if "settings" in data: 308 | settings = data['settings'] 309 | self.gameref.setGrav(settings['gravity']) 310 | #space.gravity = (g[0], g[1]) 311 | if "startID" in settings: 312 | sid = settings['startID'] 313 | if sid != -1: 314 | self.gameref.startID = idConvDict[sid] 315 | if "finishID" in settings: 316 | fid = settings['finishID'] 317 | if fid != -1 and fid in idConvDict: 318 | self.gameref.finishID = idConvDict[fid] 319 | if "killMomem" in settings: 320 | km = settings['killMomem'] 321 | if not self.gameref.mainTools.killMomem and km: 322 | self.gameref.mainTools.momemPressed() 323 | if "paused" in settings: 324 | pm = settings['paused'] 325 | if not self.gameref.mainTools.paused: self.gameref.mainTools.paused = pm -------------------------------------------------------------------------------- /kivented.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | #:import SlideTransition kivy.uix.screenmanager.SlideTransition 3 | #:set COLOR_HIGHLIGHT (0.788235294, 0.643137255, 1) 4 | #:set COLOR_BACKGROUND (0.349019608, 0.082352941, 0.658823529) 5 | #:set COLOR_BACKGROUND_A50 (0.349019608, 0.082352941, 0.658823529, .5) 6 | #:set COLOR_BORDER (0.643137255, 0.160784314, 1) 7 | #:set COLOR_BORDER_A50 (0.643137255, 0.160784314, 1, .5) 8 | #:set COLOR_BORDER_A75 (0.643137255, 0.160784314, 1, .75) 9 | #:set COLOR_HIGHLIGHT_A50 (0.788235294, 0.643137255, 1, .5) 10 | #:set COLOR_HIGHLIGHT_A100 (0.788235294, 0.643137255, 1, 1.0) 11 | #:set COLOR_BACKGROUND_A100 (0.349019608, 0.082352941, 0.658823529, 1.0) 12 | #:set COLOR_BORDER_A100 (0.643137255, 0.160784314, 1, 1.0) 13 | 14 | TestGame: 15 | 16 | : 17 | gameworld: gameworld 18 | GameWorld: 19 | id: gameworld 20 | gamescreenmanager: gamescreenmanager 21 | PositionSystem: 22 | system_id: 'position' 23 | gameworld: gameworld 24 | RotateSystem: 25 | system_id: 'rotate' 26 | gameworld: gameworld 27 | ColorSystem: 28 | system_id: 'color' 29 | gameworld: gameworld 30 | GameView: 31 | system_id: 'gameview' 32 | focus_entity: False 33 | updateable: True 34 | gameworld: gameworld 35 | pos: root.pos 36 | do_scroll: True 37 | do_scroll_lock: False 38 | size: root.size 39 | camera_scale: 2.0 40 | GameMap: 41 | system_id: 'map' 42 | map_size: root.size 43 | gameworld: gameworld 44 | Renderer: 45 | gameworld: gameworld 46 | system_id: 'renderer' 47 | do_rotate: True 48 | do_color: True 49 | shader_source: 'assets/glsl/positionrotateshader.glsl' 50 | atlas_dir: 'assets/' 51 | atlas: 'myatlas' 52 | DynamicRenderer: 53 | gameworld: gameworld 54 | system_id: 'physics_renderer' 55 | do_color: True 56 | shader_source: 'assets/glsl/positionrotateshader.glsl' 57 | atlas_dir: 'assets/' 58 | atlas: 'myatlas' 59 | physics_system: 'physics' 60 | StaticVertMeshRenderer: 61 | gameworld: gameworld 62 | system_id: 'poly_renderer' 63 | shader_source: 'assets/shaders/poscolorshader.glsl' 64 | vertex_data_count: 6 65 | CymunkPhysics: 66 | gameworld: root.gameworld 67 | system_id: 'physics' 68 | viewport: 'gameview' 69 | GameScreenManager: 70 | id: gamescreenmanager 71 | size: root.size 72 | pos: root.pos 73 | gameworld: gameworld 74 | 75 | : 76 | MainScreen: 77 | id: main_screen 78 | 79 | 80 | multiline: False 81 | 82 | multiline: False 83 | 84 | radiusLabel:radiusLabel 85 | size_hint_y:None 86 | height:30 87 | BoxLayout: 88 | Label: 89 | text: "radius:" 90 | FloatInput: 91 | id: radiusLabel 92 | multiline: False 93 | text: "radius" 94 | 95 | orientation: 'vertical' 96 | widthLabel:widthLabel 97 | heightLabel:heightLabel 98 | spacing: 4 99 | #size_hint_y:None 100 | #height:60 101 | #rows_minimum:60 102 | BoxLayout: 103 | Label: 104 | text: "width:" 105 | FloatInput: 106 | id: widthLabel 107 | multiline: False 108 | text: "width" 109 | BoxLayout: 110 | Label: 111 | text: "height:" 112 | FloatInput: 113 | id: heightLabel 114 | multiline: False 115 | text: "height" 116 | 117 | size_hint_y: None 118 | height: 30 119 | keyLabel: keyLabel 120 | valueLabel: valueLabel 121 | Label: 122 | id: keyLabel 123 | text: "" 124 | #values: app.root.mainTools.data_key_types 125 | #on_text: root.typeChanged(self) 126 | Label: 127 | text:"=" 128 | size_hint_x: None 129 | width: self.height 130 | TextInput: 131 | id: valueLabel 132 | multiline: False 133 | text: "" 134 | on_text: root.valueChanged(self) 135 | 136 | orientation: 'vertical' 137 | dataItems: dataItems 138 | BoxLayout: 139 | id: dataItems 140 | Button: 141 | size_hint_y: None 142 | height: 30 143 | text: "Add Item" 144 | on_press: root.newItem()#dataItems.add_widget(entDataItem()) 145 | 146 | 147 | orientation: 'horizontal' 148 | nameLabel: nameLabel 149 | authorLabel: authorLabel 150 | createdLabel: createdLabel 151 | modifiedLabel: modifiedLabel 152 | downloadButton: downloadButton 153 | downloadsLabel: downloadsLabel 154 | screenShot: screenShot 155 | AsyncImage: 156 | id:screenShot 157 | Label: 158 | id: nameLabel 159 | text: "Name" 160 | #Label: 161 | # text: "Rating" 162 | Label: 163 | id: authorLabel 164 | text: "Author" 165 | Label: 166 | id: createdLabel 167 | text: "Created" 168 | Label: 169 | id: modifiedLabel 170 | text: "Modified" 171 | Button: 172 | id: downloadButton 173 | text: "Download" 174 | Label: 175 | id: downloadsLabel 176 | text: "Downloads" 177 | 178 | 179 | orientation: 'vertical' 180 | levelBox: levelBox 181 | sortSpinner: sortSpinner 182 | reverseButton: reverseButton 183 | 184 | BoxLayout: 185 | size_hint_y: 0.1 186 | orientation: 'horizontal' 187 | Label: 188 | text: "sort by:" 189 | Spinner: 190 | id: sortSpinner 191 | text: "dateAdded" 192 | values: ['name', 'author', 'rating', 'dateAdded', 'downloads', 'dateModified'] 193 | #on_text: root.typeChanged(self) 194 | 195 | ToggleButton: 196 | id: reverseButton 197 | text:"reverse" 198 | state: "down" 199 | Button: 200 | text:"GO" 201 | on_press: root.goPressed(self) 202 | BoxLayout: 203 | size_hint_y: 0.1 204 | orientation: 'horizontal' 205 | Label: 206 | Button: 207 | text:"Name" 208 | on_press: root.setSort('name') 209 | Button: 210 | on_press: root.setSort('author') 211 | text: "Author" 212 | Button: 213 | on_press: root.setSort('dateAdded') 214 | text: "Created" 215 | Button: 216 | on_press: root.setSort('dateModified') 217 | text: "Modified" 218 | Label: 219 | Button: 220 | on_press: root.setSort('downloads') 221 | text: "Downloads" 222 | BoxLayout: 223 | id: levelBox 224 | orientation: 'vertical' 225 | BoxLayout: 226 | size_hint_y: 0.1 227 | orientation: 'horizontal' 228 | Button: 229 | text: "previous" 230 | on_press: root.prevPage() 231 | Button: 232 | text: "next" 233 | on_press: root.nextPage() 234 | 235 | 236 | orientation: 'vertical' 237 | 238 | nameLabel: nameLabel 239 | #screenShot: screenShot 240 | 241 | 242 | BoxLayout: 243 | orientation: 'horizontal' 244 | Label: 245 | text: "Name" 246 | tbox: 247 | id: nameLabel 248 | text:"" 249 | #Image: 250 | # id:screenShot 251 | Button: 252 | text:"Save" 253 | on_press: root.savePressed(self) 254 | 255 | orientation: 'vertical' 256 | userName: userName 257 | password: password 258 | 259 | nameLabel: nameLabel 260 | screenShot: screenShot 261 | descLabel: descLabel 262 | 263 | BoxLayout: 264 | Button: 265 | text:"" 266 | size_hint_x: 0 267 | width: 0 268 | tbox: 269 | id: userName 270 | text: "Guest" 271 | tbox: 272 | id: password 273 | text: "nopassword" 274 | Button: 275 | text:"Check/Create" 276 | #size_hint_x: None 277 | #width: self.height 278 | on_press: root.userPressed(self) 279 | Label: 280 | id: nameLabel 281 | text:"LevelName" 282 | Image: 283 | id:screenShot 284 | #tags? #compatible games? 285 | tbox: 286 | id: descLabel 287 | text:"Description" 288 | Button: 289 | text:"UpLoad!" 290 | on_press: root.uploadPressed(self) 291 | 292 | 293 | orientation: 'vertical' 294 | colTypeASpinner: colTypeASpinner 295 | colTypeBSpinner: colTypeBSpinner 296 | 297 | beginSpinner: beginSpinner 298 | pre_solveSpinner: pre_solveSpinner 299 | post_solveSpinner: post_solveSpinner 300 | separateSpinner: separateSpinner 301 | 302 | BoxLayout: 303 | Spinner: 304 | id: colTypeASpinner 305 | text: "default" 306 | values: app.root.mainTools.col_types 307 | on_text: root.typeChanged(self) 308 | Button: 309 | text:"+" 310 | size_hint_x: None 311 | width: self.height 312 | on_press: root.newType(self) 313 | Label: 314 | text: "and" 315 | #begin=None, pre_solve=None, post_solve=None, separate=None 316 | Spinner: 317 | id: colTypeBSpinner 318 | text: "default" 319 | values: app.root.mainTools.col_types 320 | on_text: root.typeChanged(self) 321 | BoxLayout: 322 | Label: 323 | text:"Start Touching (begin)" 324 | Spinner: 325 | id: beginSpinner 326 | values: app.root.mainTools.col_funcs 327 | on_text: root.calleeChanged(self, "begin") 328 | BoxLayout: 329 | Label: 330 | text:"Are Touching (pre_solve)" 331 | Spinner: 332 | id: pre_solveSpinner 333 | values: app.root.mainTools.col_funcs 334 | on_text: root.calleeChanged(self, "pre_solve") 335 | BoxLayout: 336 | Label: 337 | text:"Are Touching (post_solve)" 338 | Spinner: 339 | id: post_solveSpinner 340 | values: app.root.mainTools.col_funcs 341 | on_text: root.calleeChanged(self, "post_solve") 342 | BoxLayout: 343 | Label: 344 | text:"Stop Touching (separate)" 345 | Spinner: 346 | id: separateSpinner 347 | values: app.root.mainTools.col_funcs 348 | on_text: root.calleeChanged(self, "separate") 349 | 350 | 351 | 352 | id:mtid 353 | leftMenu: leftMenu 354 | rightMenu: rightMenu 355 | createMenu: createMenu.__self__ 356 | polyMenu: polyMenu.__self__ 357 | splineMenu: splineMenu.__self__ 358 | rootMenu: rootMenu 359 | joinMenu: joinMenu.__self__ 360 | entityMenu: entityMenu.__self__ 361 | massSlider: massSlider 362 | gravxSlider: gravxSlider 363 | gravySlider: gravySlider 364 | spriteSpinner: spriteSpinner 365 | selectedMenu: selectedMenu 366 | spritePreview: spritePreview 367 | nameBox: nameBox 368 | selectedMenuView: selectedMenuView.__self__ 369 | fileMenu: fileMenu.__self__ 370 | settingsMenu: settingsMenu.__self__ 371 | examplesMenu: examplesMenu.__self__ 372 | levelsMenu: levelsMenu.__self__ 373 | inputPreview: inputPreview 374 | cloneSpriteButton: cloneSpriteButton 375 | 376 | FloatLayout: 377 | id: topMenu 378 | size_hint: (.6, .2) 379 | orientation: 'horizontal' 380 | pos_hint: {'x':.2, 'y':.8} 381 | Label: 382 | id: inputPreview 383 | pos_hint: {'x':.2, 'y':.4} 384 | size_hint: (.6, .2) 385 | text: "" 386 | 387 | 388 | BoxLayout: 389 | id: rightMenu 390 | size_hint: (.15, 1) 391 | orientation: 'horizontal' 392 | pos_hint: {'x':.85, 'y':0} 393 | ScrollView: 394 | id: selectedMenuView 395 | GridLayout: 396 | cols: 1 397 | spacing: 4 398 | size_hint_y: None 399 | row_default_height: '30sp' 400 | row_force_default: False 401 | id: selectedMenu 402 | selectedLabel: selectedLabel 403 | xposLabel: xposLabel 404 | yposLabel: yposLabel 405 | xvelLabel: xvelLabel 406 | yvelLabel: yvelLabel 407 | angleLabel: angleLabel 408 | shapeInfo: shapeInfo 409 | redLabel: redLabel 410 | greenLabel: greenLabel 411 | blueLabel: blueLabel 412 | opacityLabel: opacityLabel 413 | height: self.minimum_height 414 | frictionLabel: frictionLabel 415 | massLabel: massLabel 416 | elasLabel: elasLabel 417 | texLabel: texLabel 418 | imgWidthLabel: imgWidthLabel 419 | imgHeightLabel: imgHeightLabel 420 | colTypeSpinner: colTypeSpinner 421 | delButton: delButton 422 | Label: 423 | id: selectedLabel 424 | text: "selected item" 425 | BoxLayout: 426 | #height:100 427 | Label: 428 | text: "x:" 429 | FloatInput: 430 | id: xposLabel 431 | text: "position" 432 | on_text: if root.fireText: root.xposChanged(self) 433 | BoxLayout: 434 | Label: 435 | text: "y:" 436 | FloatInput: 437 | id: yposLabel 438 | text: "position" 439 | on_text: if root.fireText: root.yposChanged(self) 440 | BoxLayout: 441 | Label: 442 | text: "xvel:" 443 | FloatInput: 444 | id: xvelLabel 445 | on_text: if root.fireText: root.xvelChanged(self) 446 | BoxLayout: 447 | Label: 448 | text: "yvel:" 449 | FloatInput: 450 | id: yvelLabel 451 | on_text: if root.fireText: root.yvelChanged(self) 452 | BoxLayout: 453 | Label: 454 | text: "angle:" 455 | FloatInput: 456 | id: angleLabel 457 | text: "angle" 458 | on_text: if root.fireText: root.angleChanged(self) 459 | Spinner: 460 | id: texLabel 461 | text: "texture" 462 | on_text: if root.fireText: root.textureChanged(self) 463 | values: root.sprite_list 464 | BoxLayout: 465 | size_hint_y: None 466 | height: 60 467 | BoxLayout: 468 | orientation: 'vertical' 469 | Label: 470 | text: 'dwidth' 471 | FloatInput: 472 | id: imgWidthLabel 473 | text: '1' 474 | on_text: if root.fireText: root.imgWidthChanged(self.text) 475 | BoxLayout: 476 | orientation: 'vertical' 477 | Label: 478 | text: 'dheight' 479 | FloatInput: 480 | id: imgHeightLabel 481 | text: '1' 482 | on_text: if root.fireText: root.imgHeightChanged(self.text) 483 | BoxLayout: 484 | size_hint_y: None 485 | height: 60 486 | BoxLayout: 487 | orientation: 'vertical' 488 | Label: 489 | text: 'R' 490 | FloatInput: 491 | id: redLabel 492 | text: '1' 493 | on_text: if root.fireText: root.redChanged(self.text) 494 | BoxLayout: 495 | orientation: 'vertical' 496 | Label: 497 | text: 'G' 498 | FloatInput: 499 | id: greenLabel 500 | text: '1' 501 | on_text: if root.fireText: root.greenChanged(self.text) 502 | BoxLayout: 503 | orientation: 'vertical' 504 | Label: 505 | text: 'B' 506 | FloatInput: 507 | id: blueLabel 508 | text: '1' 509 | on_text: if root.fireText: root.blueChanged(self.text) 510 | BoxLayout: 511 | orientation: 'vertical' 512 | Label: 513 | text: 'O' 514 | FloatInput: 515 | id: opacityLabel 516 | text: '1' 517 | on_text: if root.fireText: root.opacityChanged(self.text) 518 | BoxLayout: 519 | Label: 520 | text: "Mass:" 521 | FloatInput: 522 | id: massLabel 523 | text: "Mass" 524 | on_text: if root.fireText: root.massChanged(self) 525 | BoxLayout: 526 | Label: 527 | text: "Friction:" 528 | FloatInput: 529 | id: frictionLabel 530 | text: "Friction" 531 | on_text: if root.fireText: root.frictionChanged(self) 532 | BoxLayout: 533 | Label: 534 | text: "Elasticity:" 535 | FloatInput: 536 | id: elasLabel 537 | text: "Elasticity" 538 | on_text: if root.fireText: root.elasChanged(self) 539 | #ToggleButton: 540 | # id: sensorButton 541 | # text:"Sensor" 542 | # on_press: root.sensorPressed(self) 543 | BoxLayout: 544 | id: shapeInfo 545 | size_hint_y:None 546 | height:60 547 | orientation: 'vertical' 548 | Spinner: 549 | id: colTypeSpinner 550 | text: "default" 551 | on_text: if root.fireText: root.colTypeChanged(self) 552 | values: root.col_types 553 | Button: 554 | id: delButton 555 | text: 'delete' 556 | on_press: root.delSelPressed(self) 557 | Button: 558 | id: copyButton 559 | text: 'copy' 560 | on_press: root.entcpy = root.gameref.serials.entToDict(root.selectedEntity) 561 | Button: 562 | text: 'Variables' 563 | on_press: root.varsPressed(self) 564 | 565 | BoxLayout: 566 | id: leftMenu 567 | momemButton:momemButton 568 | size_hint: (.15, 1) 569 | orientation: 'horizontal' 570 | BoxLayout: 571 | id: rootMenu 572 | orientation: 'vertical' 573 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 574 | padding: 4 575 | height: root.height 576 | Button: 577 | text: 'File' 578 | on_press: root.changel2menu(root.fileMenu) 579 | Button: 580 | text: 'Settings' 581 | on_press: root.changel2menu(root.settingsMenu) 582 | Label: 583 | text: 'Quick' 584 | size_hint_y: None 585 | height: self.font_size+6 586 | text_size: (self.width, self.height) 587 | Button: 588 | text: 'Quick Load' 589 | on_press: root.loadPressed(self) 590 | Button: 591 | text: 'Quick Save' 592 | on_press: root.savePressed(self) 593 | Label: 594 | text: 'Create' 595 | size_hint_y: None 596 | height: self.font_size+6 597 | text_size: (self.width, self.height) 598 | Button: 599 | text: 'Sprite' 600 | on_press: root.changel2menu(root.createMenu) 601 | Button: 602 | text: 'Polygon' 603 | on_press: root.changel2menu(root.polyMenu) 604 | Button: 605 | text: 'Spline' 606 | on_press: root.changel2menu(root.splineMenu) 607 | Button: 608 | text: 'Start/End' 609 | on_press: root.changel2menu(root.entityMenu) 610 | Button: 611 | id: joinButton 612 | text: 'Joint' 613 | on_press: root.changel2menu(root.joinMenu) 614 | ToggleButton: 615 | group: 'ToolGroup' 616 | id: pasteButton 617 | text: 'Paste' 618 | on_press: root.setTool("paste") 619 | Label: 620 | text: 'Interact:' 621 | size_hint_y: None 622 | height: self.font_size+6 623 | text_size: (self.width, self.height) 624 | ToggleButton: 625 | group: 'ToolGroup' 626 | id: camButton 627 | text: 'Camera' 628 | on_press: root.setTool("camera") 629 | ToggleButton: 630 | group: 'ToolGroup' 631 | id: vortexButton 632 | text: 'Vortex' 633 | on_press: root.setTool("vortex") 634 | ToggleButton: 635 | group: 'ToolGroup' 636 | id: dragButton 637 | text: 'Drag' 638 | on_press: root.setTool("drag") 639 | ToggleButton: 640 | group: 'ToolGroup' 641 | id: rotateButton 642 | text: 'Rotate' 643 | on_press: root.setTool("rotate") 644 | ToggleButton: 645 | group: 'ToolGroup' 646 | id: deleteButton 647 | text: 'Delete' 648 | on_press: root.setTool("del") 649 | ToggleButton: 650 | id: playButton 651 | text: 'Pause' 652 | on_press: root.playPressed(self) 653 | ToggleButton: 654 | id: momemButton 655 | text: 'NoMomentum' 656 | on_press: root.momemPressed(self) 657 | Button: 658 | id: Button 659 | text: 'scripts' 660 | on_press: root.scriptsPressed(self) 661 | BoxLayout: 662 | id: entityMenu 663 | orientation: 'vertical' 664 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 665 | padding: 4 666 | height: root.height 667 | ToggleButton: 668 | group: 'ToolGroup' 669 | id: startButton 670 | text: 'Start' 671 | on_press: root.setTool("start") 672 | ToggleButton: 673 | group: 'ToolGroup' 674 | id: endButton 675 | text: 'End' 676 | on_press: root.setTool("end") 677 | ToggleButton: 678 | group: 'ToolGroup' 679 | id: blankButton 680 | text: 'Blank' 681 | on_press: root.setTool("blank") 682 | BoxLayout: 683 | id: polyMenu 684 | orientation: 'vertical' 685 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 686 | padding: 4 687 | height: root.height 688 | brushSizeSlider: brushSizeSlider 689 | polyMergeButton: polyMergeButton 690 | polySimpleButton: polySimpleButton 691 | polyPhysButton: polyPhysButton 692 | minlenslider: minlenslider 693 | ToggleButton: 694 | group: 'ToolGroup' 695 | id: polyButton 696 | text: 'PolyPaint' 697 | on_press: root.setTool("poly") 698 | ToggleButton: 699 | group: 'ToolGroup' 700 | id: polysubButton 701 | text: 'PolyErase' 702 | on_press: root.setTool("polysub") 703 | Label: 704 | text: 'Settings:' 705 | ToggleButton: 706 | id: polyMergeButton 707 | text: 'Merge Start' 708 | state: 'down' 709 | height: self.font_size+6 710 | ToggleButton: 711 | id: polyPhysButton 712 | text: 'NoPhysics' 713 | #state: 'down' 714 | height: self.font_size+6 715 | ToggleButton: 716 | id: polySimpleButton 717 | text: 'One Outline' 718 | height: self.font_size+6 719 | BoxLayout: 720 | orientation: 'horizontal' 721 | Label: 722 | text: "Brush" 723 | #on_press: root.massPressed(self) 724 | Slider: 725 | id: brushSizeSlider 726 | orientation: "vertical" 727 | min:5 728 | max:500 729 | value:25 730 | on_value: root.brush_size_changed(self.value) 731 | Label: 732 | size_hint_y: None 733 | height: self.font_size+5 734 | text: 'MinLen:' 735 | Slider: 736 | size_hint_y: None 737 | height: 30 738 | id: minlenslider 739 | orientation: "horizontal" 740 | min:1 741 | max:80 742 | value:5 743 | BoxLayout: 744 | id: splineMenu 745 | orientation: 'vertical' 746 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 747 | padding: 4 748 | height: root.height 749 | splineButton: splineButton 750 | splineEdButton: splineEdButton 751 | removePointButton: removePointButton 752 | smoothnessSlider: smoothnessSlider 753 | ToggleButton: 754 | group: 'ToolGroup' 755 | id: splineButton 756 | text: 'New Spline' 757 | on_press: root.setTool("spline") 758 | ToggleButton: 759 | group: 'ToolGroup' 760 | id: splineEdButton 761 | text: 'Edit Spline' 762 | on_press: root.setTool("splineed") 763 | ToggleButton: 764 | group: 'ToolGroup' 765 | id: removePointButton 766 | text: 'Remove Point' 767 | on_press: root.setTool("splinesub") 768 | Label: 769 | text: 'Settings:' 770 | ToggleButton: 771 | id: splinePhysButton 772 | text: 'NoPhysics' 773 | #state: 'down' 774 | height: self.font_size+6 775 | BoxLayout: 776 | orientation: 'horizontal' 777 | Label: 778 | text: "Smoothness" 779 | #on_press: root.massPressed(self) 780 | Slider: 781 | id: smoothnessSlider 782 | orientation: "vertical" 783 | min:1 784 | max:10 785 | value:4 786 | #on_value: root.brush_size_changed(self.value) 787 | BoxLayout: 788 | id: createMenu 789 | orientation: 'vertical' 790 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 791 | padding: 4 792 | height: root.height 793 | spritePhysButton: spritePhysButton 794 | ToggleButton: 795 | group: 'ToolGroup' 796 | id: circleButton 797 | text: 'Circle' 798 | on_press: root.setTool("circle") 799 | ToggleButton: 800 | group: 'ToolGroup' 801 | id: squareButton 802 | text: 'Square' 803 | on_press: root.setTool("square") 804 | ToggleButton: 805 | group: 'ToolGroup' 806 | id: boxButton 807 | text: 'Box' 808 | on_press: root.setTool("box") 809 | ToggleButton: 810 | group: 'ToolGroup' 811 | id: linesButton 812 | text: 'Lines' 813 | on_press: root.setTool("draw") 814 | ToggleButton: 815 | group: 'ToolGroup' 816 | id: plankButton 817 | text: 'Plank' 818 | on_press: root.setTool("plank") 819 | Label: 820 | size_hint: (1,0.2) 821 | text: "Settings" 822 | ToggleButton: 823 | id: spritePhysButton 824 | text: 'NoPhysics' 825 | #state: 'down' 826 | height: self.font_size+6 827 | Label: 828 | size_hint: (1,0.2) 829 | text: " Image:" 830 | Spinner: 831 | id: spriteSpinner 832 | values: root.sprite_list 833 | on_text: root.spritePreview.source = 'atlas://assets/myatlas/'+self.text 834 | Image: 835 | id: spritePreview 836 | source: 'atlas://assets/myatlas/sheep' 837 | BoxLayout: 838 | orientation: 'horizontal' 839 | Button: 840 | text: "Mass" 841 | on_press: root.massPressed(self) 842 | Slider: 843 | id: massSlider 844 | orientation: "vertical" 845 | min:0 846 | max:100 847 | value:10 848 | ToggleButton: 849 | id: cloneSpriteButton 850 | text: 'clone mode' 851 | state: 'down' 852 | 853 | BoxLayout: 854 | id: joinMenu 855 | orientation: 'vertical' 856 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 857 | padding: 4 858 | height: root.height 859 | ToggleButton: 860 | group: 'ToolGroup' 861 | text: 'Pin to World' 862 | on_press: root.setTool("pin") 863 | ToggleButton: 864 | group: 'ToolGroup' 865 | text: 'Point 2 Point' 866 | on_press: root.setTool("p2p") 867 | ToggleButton: 868 | group: 'ToolGroup' 869 | text: 'P2P Spring' 870 | on_press: root.setTool("p2ps") 871 | ToggleButton: 872 | group: 'ToolGroup' 873 | text: 'Center 2 Point' 874 | on_press: root.setTool("c2p") 875 | ToggleButton: 876 | group: 'ToolGroup' 877 | text: 'Center 2 Center' 878 | on_press: root.setTool("c2c") 879 | 880 | BoxLayout: 881 | id: examplesMenu 882 | orientation: 'vertical' 883 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 884 | padding: 4 885 | height: root.height 886 | BoxLayout: 887 | id: levelsMenu 888 | orientation: 'vertical' 889 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 890 | padding: 4 891 | height: root.height 892 | BoxLayout: 893 | id: fileMenu 894 | orientation: 'vertical' 895 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 896 | padding: 4 897 | height: root.height 898 | Label: 899 | size_hint_y: None 900 | height:40 901 | id: nameBox 902 | text: 'defaultlevel' 903 | Button: 904 | text: 'Clear' 905 | on_press: root.clearPressed(self) 906 | Button: 907 | text: 'Load' 908 | on_press: root.customlvlPressed() 909 | Button: 910 | text: 'Examples' 911 | on_press: root.changel3menu(root.examplesMenu) 912 | Button: 913 | text: 'Save As...' 914 | on_press: root.saveAsPressed(self) 915 | Button: 916 | text: 'Export Wheelz' 917 | on_press: root.wheelzPressed(self) 918 | Button: 919 | text: 'Download' 920 | on_press: root.downloadsPressed(self) 921 | Button: 922 | text: 'Upload' 923 | on_press: root.uploadPressed(self) 924 | BoxLayout: 925 | id: settingsMenu 926 | orientation: 'vertical' 927 | pos_hint: {'x': 0., 'y': 1.0 - self.size_hint[1]} 928 | padding: 4 929 | height: root.height 930 | BoxLayout: 931 | orientation: 'vertical' 932 | Label: 933 | text: "Gravity" 934 | size_hint: (1, .2) 935 | Button: 936 | text: 'reset' 937 | size_hint: (1, .2) 938 | on_press: gravySlider.value = 0; gravxSlider.value = 0 939 | Slider: 940 | id: gravySlider 941 | orientation: "vertical" 942 | min:-1000 943 | max:1000 944 | value:0 945 | on_value: root.setygrav(self.value) 946 | Slider: 947 | id: gravxSlider 948 | size_hint: (2, .2) 949 | orientation: "horizontal" 950 | min:-1000 951 | max:1000 952 | value:0 953 | on_value: root.setxgrav(self.value) 954 | 955 | : 956 | 957 | 958 | : 959 | name: 'main' 960 | FloatLayout: 961 | size: root.size 962 | pos: root.pos 963 | MainTools: 964 | id: mainTools 965 | 966 | -------------------------------------------------------------------------------- /ui_elements.py: -------------------------------------------------------------------------------- 1 | import cymunk as cy 2 | 3 | import os 4 | import glob 5 | import re 6 | 7 | import math 8 | 9 | import json 10 | 11 | from kivy.clock import Clock 12 | from kivy.uix.button import Button 13 | from kivy.uix.boxlayout import BoxLayout 14 | from kivy.uix.floatlayout import FloatLayout 15 | from kivy.uix.textinput import TextInput 16 | from kivy.uix.popup import Popup 17 | from kivy.uix.label import Label 18 | from kivy.properties import ListProperty 19 | 20 | serverURL = 'http://www.kiventedserve.chozabu.net' 21 | #if 'chozabu' in os.getcwd():serverURL = 'http://0.0.0.0:8080' 22 | 23 | class FloatInput(TextInput): 24 | def delete_selection(self, from_undo=False): 25 | if self.selection_text == self.text: 26 | self.text = '0' 27 | else: super(FloatInput, self).delete_selection(from_undo=from_undo) 28 | def do_backspace(self, from_undo=False, mode='bkspc'): 29 | if len(self.text)<2: 30 | self.text = '0' 31 | else: super(FloatInput, self).do_backspace(from_undo=from_undo, mode=mode) 32 | 33 | 34 | def insert_text(self, substring, from_undo=False): 35 | try: 36 | float(self.text+substring) 37 | except ValueError: 38 | if substring != '-':substring="" 39 | return super(FloatInput, self).insert_text(substring, from_undo=from_undo) 40 | 41 | class PlainButton(Button): 42 | def on_touch_down(self, touch): 43 | sres = super(PlainButton, self).on_touch_down(touch) 44 | print "pb=", sres 45 | return sres 46 | 47 | class entDataItem(BoxLayout): 48 | def __init__(self,iname,prnt): 49 | super(entDataItem, self).__init__() 50 | self.ddict = prnt.ddict 51 | self.iname = iname 52 | self.keyLabel.text = iname 53 | if iname in self.ddict: self.valueLabel.text = str(self.ddict[iname]) 54 | 55 | 56 | def valueChanged(self, instance): 57 | self.ddict[self.iname] = instance.text 58 | #print self.ddict 59 | #def newType(self, btn): 60 | # Popup(title="Create New Collision Type", 61 | # content=TextInput(focus=True,multiline = False), 62 | # size_hint=(0.6, None), height=100, 63 | # on_dismiss=self.setNewType).open() 64 | # 65 | #def setNewType(self, popup): 66 | # pw = self.get_parent_window() 67 | # print pw 68 | # pw.children[-1].mainTools.data_key_types.append(popup.content.text) 69 | # #self.button.text = popup.content.text 70 | class entDataBox(BoxLayout): 71 | def __init__(self, ddict): 72 | super(entDataBox, self).__init__() 73 | print ddict 74 | self.ddict = ddict 75 | for key in ddict.keys(): 76 | self.add_widget(entDataItem(iname=key, prnt=self)) 77 | 78 | def newItem(self): 79 | Popup(title="Create New Variable", 80 | content=TextInput(focus=True,multiline = False), 81 | size_hint=(0.6, None), height=100, 82 | on_dismiss=self.setNewVar).open() 83 | 84 | def setNewVar(self, popup): 85 | #pw = self.get_parent_window() 86 | #print pw 87 | #pw.children[-1].mainTools.data_key_types.append() 88 | #self.button.text = popup.content.text 89 | iname = popup.content.text 90 | print iname 91 | if iname in self.ddict: 92 | Popup(title="Create New Variable", 93 | content=Label(text="variable already exists"), 94 | size_hint=(0.6, None), height=100).open() 95 | return 96 | self.ddict[iname] = "" 97 | self.add_widget(entDataItem(iname=iname, prnt=self)) 98 | #def __init__(self, mtref): 99 | import urllib 100 | from kivy.network.urlrequest import UrlRequest 101 | class levelItem(BoxLayout): 102 | def __init__(self, info=None,callback=None): 103 | super(levelItem,self).__init__() 104 | self.info = info 105 | self.callback = callback 106 | Clock.schedule_once(self.initUI) 107 | def initUI(self, dt): 108 | if self.info == None: 109 | self.remove_widget(self.downloadButton) 110 | self.add_widget(Label(text="Downloads")) 111 | return 112 | self.nameLabel.text = self.info['name'] 113 | self.authorLabel.text = self.info['author'] 114 | self.createdLabel.text = str(self.info['dateAdded'])[:10] 115 | self.modifiedLabel.text = str(self.info['dateModified'])[:10] 116 | self.downloadsLabel.text = str(self.info['downloads'])[:10] 117 | self.downloadButton.bind(on_press=self.callback) 118 | self.downloadButton.info = self.info 119 | self.screenShot.source = serverURL+"/downloadSS?fullname="+self.info['filename']+".png" 120 | class saveas(BoxLayout): 121 | def __init__(self, mtref): 122 | self.mtref = mtref 123 | super(saveas,self).__init__() 124 | def initUI(self, dt=0): 125 | self.nameLabel.text = self.mtref.nameBox.text 126 | def savePressed(self, instance): 127 | self.mtref.nameBox.text = self.nameLabel.text 128 | self.mtref.savePressed() 129 | self.mtref.sapopup.dismiss() 130 | Popup(title="Saved", 131 | content=Label(text="saved "+self.nameLabel.text), 132 | size_hint=(0.6, 0.3), size=(400, 400)).open() 133 | class uploads(BoxLayout): 134 | def __init__(self, mtref): 135 | self.mtref = mtref 136 | super(uploads,self).__init__() 137 | #Clock.schedule_once(self.initUI) 138 | def initUI(self, dt=0): 139 | self.nameLabel.text = self.mtref.nameBox.text 140 | self.screenShot.source = self.nameLabel.text+".png" 141 | self.screenShot.reload() 142 | #descLabel: descLabel 143 | #self.gameref.export_to_png(filename=self.nameBox.text+".png") 144 | 145 | #wget -qO- --post-data "userName=alex&passHash=123&name=test2&creating=true" http://0.0.0.0:8080/createUser 146 | def userPressed(self, instance): 147 | params = urllib.urlencode({ 148 | 'userName':self.userName.text, 'passHash': self.password.text, 'creating':"true" 149 | }) 150 | headers = {'Content-type': 'application/x-www-form-urlencoded', 151 | 'Accept': 'text/plain'} 152 | req = UrlRequest(serverURL+'/createUser', on_success=self.user_posted, req_body=params, 153 | req_headers=headers) 154 | req.wait() 155 | def user_posted(self, info, result): 156 | print "sent user" 157 | print info 158 | print result 159 | #self.mtref.ulpopup.dismiss() 160 | Popup(title="Info", 161 | content=Label(text=str(result)), 162 | size_hint=(0.6, 0.4), size=(400, 400)).open() 163 | #wget -qO- --post-data "author=alex&passHash=123&name=test2&levelData=asdagrdh" http://0.0.0.0:8080/uploadLevel 164 | def uploadPressed(self, instance): 165 | self.uploadLevel() 166 | def uploadLevel(self): 167 | print "uploading level" 168 | lname = self.mtref.nameBox.text 169 | updata = self.mtref.gameref.serials.exportDict() 170 | #req = UrlRequest('/listLevels', on_success=self.got_levels, timeout=1000) 171 | import base64 172 | params = urllib.urlencode({ 173 | 'author':self.userName.text, 'passHash': self.password.text, 174 | 'name':lname,"levelData":json.dumps(updata), 175 | "sshot":base64.b64encode(open(lname+".png", 'r').read()) 176 | }) 177 | headers = {'Content-type': 'application/x-www-form-urlencoded', 178 | 'Accept': 'text/plain'} 179 | req = UrlRequest(serverURL+'/uploadLevel', on_success=self.level_posted, req_body=params, 180 | req_headers=headers) 181 | req.wait() 182 | def level_posted(self, info, result): 183 | print "sent level" 184 | print info 185 | print result 186 | self.mtref.ulpopup.dismiss() 187 | Popup(title="Uploaded", 188 | content=Label(text=str(result)), 189 | size_hint=(0.6, 0.4), size=(400, 400)).open() 190 | class downloads(BoxLayout): 191 | def __init__(self, mtref): 192 | self.mtref = mtref 193 | super(downloads,self).__init__() 194 | self.cursor = 0 195 | self.pagesize = 8 196 | 197 | def setSort(self, stype): 198 | if stype == self.sortSpinner.text: 199 | print self.reverseButton.state 200 | if self.reverseButton.state == 'down': 201 | self.reverseButton.state = 'normal' 202 | else: 203 | self.reverseButton.state = 'down' 204 | else: 205 | self.sortSpinner.text = stype 206 | self.goPressed() 207 | def prevPage(self): 208 | self.cursor -=self.pagesize 209 | if self.cursor<0:self.cursor = 0 210 | self.listLevels() 211 | def nextPage(self): 212 | self.cursor +=self.pagesize 213 | self.listLevels() 214 | def goPressed(self, instance=None): 215 | print "go pressed" 216 | self.cursor = 0 217 | self.listLevels() 218 | def listLevels(self): 219 | headers = {'Content-type': 'application/x-www-form-urlencoded', 220 | 'Accept': 'text/plain'} 221 | 222 | params = {"cursor":self.cursor, "limit":self.pagesize,"sortKey": self.sortSpinner.text} 223 | if self.reverseButton.state == 'down':params['reverse']=True 224 | print "requesting levels", serverURL+'/queryLevels',params 225 | params = urllib.urlencode(params) 226 | print self.reverseButton.state 227 | req = UrlRequest(serverURL+'/queryLevels', on_success=self.got_levels, 228 | req_headers=headers 229 | ,on_error=self.on_error,on_failure=self.on_failure, on_redirect=self.on_redirect,req_body=params) 230 | print "waiting" 231 | req.wait() 232 | print "waited" 233 | def on_error(self, info, result): 234 | print "on_error", info, result 235 | def on_redirect(self, info, result): 236 | print "on_redirect", info, result 237 | def on_failure(self, info, result): 238 | print "on_failure", info, result 239 | def got_levels(self, info, result): 240 | print "got levels:" 241 | print info 242 | print result 243 | data = json.loads(result) 244 | print data 245 | if data['result'] == "OK": 246 | print "OK" 247 | self.setChildren(data['data']) 248 | def setChildren(self, data): 249 | #data = json.loads(data) 250 | print data 251 | self.levelBox.clear_widgets() 252 | print len(data) 253 | #self.levelBox.add_widget(levelItem()) 254 | for item in data: 255 | #print item 256 | i=item#data[item] 257 | #b = Button(text=i['name'], on_press=self.dllevel) 258 | li = levelItem(i,self.dllevel) 259 | #b.info = i 260 | self.levelBox.add_widget(li) 261 | 262 | def dllevel(self,instance): 263 | print instance.info 264 | print "making request" 265 | params = urllib.urlencode({'fullname': instance.info['filename']}) 266 | req = UrlRequest(serverURL+'/downloadLevel', on_success=self.got_level, timeout=1000, req_body=params 267 | ,on_error=self.on_error,on_failure=self.on_failure, on_redirect=self.on_redirect) 268 | req.levelname = instance.info['name'] 269 | print "made request" 270 | req.wait() 271 | print "wait over" 272 | def got_level(self, info, result): 273 | print "got level" 274 | print "info=",info 275 | print "result=",result 276 | rd = json.loads(result) 277 | dd = json.loads(rd['data']) 278 | print "--------------" 279 | print dd 280 | for i in dd: 281 | print i, dd[i] 282 | self.mtref.gameref.clearAll() 283 | self.mtref.gameref.serials.loadFromDict(dd) 284 | self.mtref.nameBox.text = info.levelname 285 | self.mtref.dlpopup.dismiss() 286 | 287 | class callbacks(BoxLayout): 288 | def __init__(self, mtref): 289 | self.mtref = mtref 290 | #self.scripty = mtref.gameref.scripty 291 | super(callbacks,self).__init__() 292 | #self.typeChanged() 293 | self.nofire = True 294 | def setTypeA(self,ta): 295 | self.colTypeASpinner.text = ta 296 | self.typeChanged() 297 | def typeChanged(self, instance=None): 298 | handlers = self.mtref.gameref.scripty.collision_handlers 299 | if self.colTypeASpinner.text not in handlers: handlers[self.colTypeASpinner.text] = {} 300 | tbd = handlers[self.colTypeASpinner.text] 301 | print "tbd=",tbd 302 | if self.colTypeBSpinner.text not in tbd: tbd[self.colTypeBSpinner.text] = {} 303 | methods = tbd[self.colTypeBSpinner.text] 304 | print "methods=", methods 305 | self.nofire = True 306 | self.beginSpinner.text = methods["begin"] if "begin" in methods else "None" 307 | self.pre_solveSpinner.text = methods["pre_solve"] if "pre_solve" in methods else "None" 308 | self.post_solveSpinner.text = methods["post_solve"] if "post_solve" in methods else "None" 309 | self.separateSpinner.text = methods["separate"] if "separate" in methods else "None" 310 | self.nofire = False 311 | 312 | def calleeChanged(self, instance, caller): 313 | if self.nofire: return 314 | print "changing col handler:",\ 315 | self.colTypeASpinner.text,\ 316 | self.colTypeBSpinner.text,\ 317 | caller,\ 318 | instance.text 319 | '''self.mtref.gameref.scripty.add_col_handler( 320 | self.colTypeASpinner.text, 321 | self.colTypeBSpinner.text, 322 | caller, 323 | instance.text 324 | )''' 325 | self.mtref.gameref.scripty.set_col_handlers( 326 | self.colTypeASpinner.text, 327 | self.colTypeBSpinner.text, 328 | begin=self.beginSpinner.text, 329 | pre_solve=self.pre_solveSpinner.text, 330 | post_solve=self.post_solveSpinner.text, 331 | separate=self.separateSpinner.text) 332 | def newType(self, btn): 333 | Popup(title="Create New Collision Type", 334 | content=TextInput(focus=True,multiline = False), 335 | size_hint=(0.6, None), height=100, 336 | on_dismiss=self.setNewType).open() 337 | 338 | def setNewType(self, popup): 339 | self.mtref.gameref.scripty.add_col_type(popup.content.text) 340 | self.colTypeASpinner.text = popup.content.text 341 | #self.button.text = popup.content.text 342 | 343 | 344 | 345 | 346 | class tbox(TextInput): 347 | def on_touch_down(self, touch): 348 | super(tbox, self).on_touch_down(touch) 349 | if self.collide_point(*touch.pos): 350 | #touch.grab( self ) 351 | return True 352 | return False 353 | 354 | 355 | class CircleSettings(BoxLayout): 356 | def on_touch_down(self, touch): 357 | super(CircleSettings, self).on_touch_down(touch) 358 | if self.collide_point(*touch.pos): 359 | #touch.grab( self ) 360 | return True 361 | return False 362 | 363 | 364 | class BoxSettings(BoxLayout): 365 | pass 366 | ''' 367 | def on_touch_down(self, touch): 368 | super(BoxSettings, self).on_touch_down(touch) 369 | if self.collide_point(*touch.pos): 370 | #touch.grab( self ) 371 | return True 372 | return False''' 373 | 374 | 375 | class MainTools(FloatLayout): 376 | col_types = ListProperty() 377 | col_funcs = ListProperty() 378 | sprite_list = ListProperty() 379 | data_key_types = ListProperty() 380 | def __init__(self, **kwargs): 381 | super(MainTools, self).__init__(**kwargs) 382 | self.grav_backup = cy.Vec2d(0,0) 383 | self.staticOn = False 384 | self.paused = False 385 | self.killMomem = False 386 | self.selectedItem = None 387 | self.selectedEntity = None 388 | self.toolSettings = {"circle": {"texture": "sheep"}, 389 | "square": {"texture": "Dirt"}, 390 | "box": {"texture": "face_box"}, 391 | "draw": {"texture": "Grass1"}, 392 | "poly": {"texture": "Grass1"}, 393 | "plank": {"texture": "plank"}, 394 | } 395 | self.callbacksBox = None 396 | self.currentTool = "" 397 | self.testsave = [] 398 | self.gameref = None 399 | self.entcpy = None #item on the clipboard 400 | self.fireText = True 401 | self.cpointids = [] 402 | #self.exampleLevels 403 | #self.col_types.append("default") 404 | #self.col_types.append("vortex") 405 | Clock.schedule_once(self.init_tools) 406 | 407 | def loadExample(self, instance): 408 | filename = os.path.dirname(__file__)+"/examples/"+instance.text+".json" 409 | self.gameref.clearAll() 410 | self.gameref.serials.loadExtJSON(filename) 411 | self.nameBox.text = instance.text 412 | def loadCustom(self, instance): 413 | self.gameref.clearAll() 414 | self.gameref.serials.loadJSON(instance.text+".json") 415 | self.nameBox.text = instance.text 416 | def customlvlPressed(self): 417 | levels = [ os.path.basename(f)[:-5] for f in glob.glob(self.gameref.dataDir+"*.json")] 418 | self.levelsMenu.clear_widgets() 419 | for levelname in levels: 420 | newb = Button(text=levelname, font_size=14) 421 | newb.bind(on_press=self.loadCustom) 422 | self.levelsMenu.add_widget(newb) 423 | self.changel3menu(self.levelsMenu) 424 | 425 | 426 | def init_tools(self, dt): 427 | self.l2menus = [self.settingsMenu, self.joinMenu, self.createMenu, self.entityMenu, 428 | self.fileMenu, self.polyMenu, self.splineMenu] 429 | self.l3menus = [self.examplesMenu,self.levelsMenu] 430 | #self.leftMenu.remove_widget(self.joinMenu) 431 | #self.spriteSpinner.text="square" 432 | self.rightMenu.remove_widget(self.selectedMenuView) 433 | self.clearl2() 434 | self.callbacksBox = callbacks(self) 435 | self.cbpopup = Popup(title="when", 436 | content=self.callbacksBox,#Label(text='Hello world'), 437 | size_hint=(0.8, 0.8), size=(400, 400)) 438 | 439 | self.downloadsBox = downloads(self) 440 | self.dlpopup = Popup(title="Levels", 441 | content=self.downloadsBox,#Label(text='Hello world'), 442 | size_hint=(0.8, 0.8), size=(400, 400)) 443 | 444 | self.uploadBox = uploads(self) 445 | self.ulpopup = Popup(title="Upload", 446 | content=self.uploadBox, 447 | size_hint=(0.8, 0.8), size=(400, 400)) 448 | 449 | self.saveasBox = saveas(self) 450 | self.sapopup = Popup(title="Save As", 451 | content=self.saveasBox, 452 | size_hint=(0.8, 0.4), size=(400, 400)) 453 | 454 | exampleLevels = [ os.path.basename(f)[:-5] for f in glob.glob(os.path.dirname(__file__)+"/examples/*.json")] 455 | for levelname in exampleLevels: 456 | newb = Button(text=levelname, font_size=14) 457 | newb.bind(on_press=self.loadExample) 458 | self.examplesMenu.add_widget(newb) 459 | #self.spriteSpinner.values = os.listdir("./sprites") 460 | 461 | def update(self, dt): 462 | self.fireText = False 463 | shape = self.selectedItem 464 | ent = self.selectedEntity 465 | #self.selectedMenu.selectedLabel.text = str(shape) 466 | #self.selectedMenu.xposLabel.text = "" 467 | #self.selectedMenu.yposLabel.text = "" 468 | if shape: 469 | if not self.selectedMenu.xvelLabel.focus: 470 | self.selectedMenu.xvelLabel.text = "%0.2f" % shape.body.velocity.x 471 | if not self.selectedMenu.yvelLabel.focus: 472 | self.selectedMenu.yvelLabel.text = "%0.2f" % shape.body.velocity.y 473 | if not self.selectedMenu.xposLabel.focus: 474 | self.selectedMenu.xposLabel.text = "%0.2f" % shape.body.position.x 475 | if not self.selectedMenu.yposLabel.focus: 476 | ypostr = "%0.2f" % shape.body.position.y 477 | self.selectedMenu.yposLabel.text = ypostr 478 | if not self.selectedMenu.angleLabel.focus: 479 | tv = "%0.2f" % shape.body.angle 480 | self.selectedMenu.angleLabel.text = tv 481 | elif ent: 482 | if not self.selectedMenu.xvelLabel.focus: 483 | self.selectedMenu.xvelLabel.text = "0" 484 | if not self.selectedMenu.yvelLabel.focus: 485 | self.selectedMenu.yvelLabel.text = "0" 486 | if hasattr(ent, 'position'): 487 | #if not self.selectedMenu.xposLabel.focus: 488 | self.selectedMenu.xposLabel.text = "%0.2f" % ent.position.x 489 | #if not self.selectedMenu.yposLabel.focus: 490 | ypostr = "%0.2f" % ent.position.y 491 | self.selectedMenu.yposLabel.text = ypostr 492 | if hasattr(ent, 'rotate'): 493 | #if not self.selectedMenu.angleLabel.focus: 494 | tv = "%0.2f" % ent.rotate.r 495 | self.selectedMenu.angleLabel.text = tv 496 | 497 | self.fireText = True 498 | #def examplesPressed(self, instance): 499 | 500 | 501 | def loadPressed(self, instance): 502 | self.gameref.clearAll() 503 | self.gameref.serials.loadJSON(self.nameBox.text+".json") 504 | #self.gameref.loadFromDict(self.testsave) 505 | 506 | def savePressed(self, instance=None): 507 | self.testsave = self.gameref.serials.exportJSON(self.nameBox.text+".json") 508 | 509 | def wheelzPressed(self, instance): 510 | fileName = self.nameBox.text+".lvl" 511 | self.gameref.serials.exportXML(fileName) 512 | from kivy.utils import platform 513 | try: 514 | if platform != 'android': 515 | bashCommand = "adb push /home/chozabu/git/KivEntEd/" + fileName + " /sdcard/xlvls/" 516 | #bashCommand = "pwd" 517 | #os.system(bashCommand) 518 | import subprocess 519 | #print bashCommand 520 | #print bashCommand.split() 521 | process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE) 522 | output = process.communicate()[0] 523 | print output 524 | #fileName = '/sdcard/xlvls/'+fileName 525 | except: 526 | print "could not push wheelz level to phone" 527 | 528 | 529 | def clearPressed(self, instance): 530 | self.gameref.clearAll() 531 | 532 | def setTool(self, tool): 533 | if self.currentTool not in self.toolSettings: 534 | self.toolSettings[self.currentTool] = {} 535 | lts = self.toolSettings[self.currentTool] 536 | lts["texture"] = self.spriteSpinner.text 537 | #self.spritePreview.source = 'atlas://assets/myatlas/'+ self.spriteSpinner.text 538 | lts["mass"] = self.massSlider.value 539 | self.currentTool = tool 540 | if tool in self.toolSettings: 541 | cts = self.toolSettings[tool] 542 | if "texture" in cts: 543 | self.spriteSpinner.text = cts["texture"] 544 | if "mass" in cts: 545 | self.massSlider.value = cts["mass"] 546 | print "Tool is now: %s" % tool 547 | 548 | def setRef(self, ref): 549 | self.gameref = ref 550 | 551 | 552 | def setygrav(self, value): 553 | space = self.gameref.space 554 | space.gravity = space.gravity.x, value 555 | def setxgrav(self, value): 556 | space = self.gameref.space 557 | space.gravity = value, space.gravity.y 558 | 559 | def xvelChanged(self, instance): 560 | self.inputPreview.text = instance.text 561 | try: 562 | fval = float(instance.text) 563 | except ValueError: 564 | return 565 | shape = self.selectedItem 566 | if shape: 567 | shape.body.velocity = (fval, shape.body.velocity.y) 568 | def yvelChanged(self, instance): 569 | self.inputPreview.text = instance.text 570 | try: 571 | fval = float(instance.text) 572 | except ValueError: 573 | return 574 | shape = self.selectedItem 575 | if shape: 576 | shape.body.velocity = (shape.body.velocity.x, fval) 577 | def xposChanged(self, instance): 578 | self.inputPreview.text = instance.text 579 | fval = float(instance.text) 580 | shape = self.selectedItem 581 | if shape: 582 | shape.body.position = (fval, shape.body.position.y) 583 | self.gameref.reindexEnt(self.selectedEntity) 584 | 585 | def yposChanged(self, instance): 586 | self.inputPreview.text = instance.text 587 | fval = float(instance.text) 588 | shape = self.selectedItem 589 | if shape: 590 | shape.body.position = (shape.body.position.x, fval) 591 | self.gameref.reindexEnt(self.selectedEntity) 592 | 593 | def angleChanged(self, instance): 594 | self.inputPreview.text = instance.text 595 | fval = float(instance.text) 596 | shape = self.selectedItem 597 | if shape: 598 | shape.body.angle = fval 599 | self.gameref.reindexEnt(self.selectedEntity) 600 | def brush_size_changed(self, value): 601 | sides = int(8+math.sqrt(value)) 602 | sidelen =value*math.pi*2/sides 603 | self.polyMenu.minlenslider.value = (sidelen-1)*0.3 604 | print sidelen 605 | 606 | 607 | def simplifyPolyPressed(self, instance): 608 | ent = self.selectedEntity 609 | if hasattr(ent, 'poly_renderer'): 610 | #ent.polyshape.remove_short_lines() 611 | ent.polyshape.remove_some_pts(.8) 612 | 613 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id) 614 | 615 | def textureChanged(self, instance): 616 | ent = self.selectedEntity 617 | if hasattr(ent, 'physics_renderer'): 618 | ent.physics_renderer.texture = instance.text 619 | elif hasattr(ent, 'poly_renderer'): 620 | tex = 'sprites/'+instance.text+'.png' 621 | ent.poly_renderer.texture = tex 622 | self.gameref.gameworld.systems['poly_renderer'].redraw_entity(ent.entity_id) 623 | else: 624 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id,texture=instance.text) 625 | 626 | def redChanged(self, strval): 627 | self.inputPreview.text = strval 628 | fval = float(strval) 629 | ent = self.selectedEntity 630 | if ent: 631 | ent.color.r = fval 632 | if self.selectedItem.__class__.__name__ == 'Poly': 633 | c = ent.color 634 | color = (c.r, c.g ,c.b ,c.a) 635 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id,color=color) 636 | def greenChanged(self, strval): 637 | self.inputPreview.text = strval 638 | fval = float(strval) 639 | ent = self.selectedEntity 640 | if ent: 641 | ent.color.g = fval 642 | if self.selectedItem.__class__.__name__ == 'Poly': 643 | c = ent.color 644 | color = (c.r, c.g ,c.b ,c.a) 645 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id,color=color) 646 | def blueChanged(self, strval): 647 | self.inputPreview.text = strval 648 | fval = float(strval) 649 | ent = self.selectedEntity 650 | if ent: 651 | ent.color.b = fval 652 | if self.selectedItem.__class__.__name__ == 'Poly': 653 | c = ent.color 654 | color = (c.r, c.g ,c.b ,c.a) 655 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id,color=color) 656 | def opacityChanged(self, strval): 657 | self.inputPreview.text = strval 658 | fval = float(strval) 659 | ent = self.selectedEntity 660 | if ent: 661 | ent.color.a = fval 662 | if self.selectedItem.__class__.__name__ == 'Poly': 663 | c = ent.color 664 | color = (c.r, c.g ,c.b ,c.a) 665 | self.gameref.create_poly((0,0),ent.polyshape,ent.entity_id,color=color) 666 | 667 | def frictionChanged(self, instance): 668 | self.inputPreview.text = instance.text 669 | fval = float(instance.text) 670 | if self.selectedEntity: 671 | for shape in self.selectedEntity.physics.shapes: 672 | shape.friction = fval 673 | self.gameref.reindexEnt(self.selectedEntity) 674 | elif self.selectedItem: 675 | self.selectedItem.friction = fval 676 | self.gameref.reindexEnt(self.selectedEntity) 677 | 678 | def massChanged(self, instance): 679 | self.inputPreview.text = instance.text 680 | fval = float(instance.text) 681 | if fval <= 0: 682 | fval = 0.1 683 | instance.text = "%0.2f" % fval 684 | shape = self.selectedItem 685 | if shape: 686 | shape.body.mass = fval 687 | self.gameref.reindexEnt(self.selectedEntity) 688 | 689 | if shape.__class__.__name__ == "BoxShape": 690 | self.selectedItem.body.moment = cy.moment_for_box(self.selectedItem.body.mass, self.selectedItem.width, self.selectedItem.height) 691 | if shape.__class__.__name__ == "Circle": 692 | self.selectedItem.body.moment = cy.moment_for_circle(self.selectedItem.body.mass, 693 | self.selectedItem.radius,0) #seems ineffective? 694 | 695 | def elasChanged(self, instance): 696 | self.inputPreview.text = instance.text 697 | fval = float(instance.text) 698 | if self.selectedEntity: 699 | for shape in self.selectedEntity.physics.shapes: 700 | shape.elasticity = fval 701 | self.gameref.reindexEnt(self.selectedEntity) 702 | elif self.selectedItem: 703 | self.selectedItem.elasticity = fval 704 | 705 | self.gameref.reindexEnt(self.selectedEntity) 706 | 707 | def on_rad_change(self, instance, value): 708 | self.inputPreview.text = instance.text 709 | newrad = float(value) 710 | print "rad change", newrad 711 | if self.selectedItem and self.selectedEntity: 712 | self.selectedItem.radius = newrad 713 | self.selectedItem.unsafe_set_radius(newrad) 714 | self.selectedEntity.physics_renderer.width = newrad * 2 715 | self.selectedEntity.physics_renderer.height = newrad * 2 716 | self.selectedItem.body.moment = cy.moment_for_circle(self.selectedItem.body.mass, 717 | newrad,0) #seems ineffective? 718 | self.gameref.reindexEnt(self.selectedEntity) 719 | 720 | def imgWidthChanged(self, value): 721 | self.inputPreview.text = value 722 | newval = float(value) 723 | ent = self.selectedEntity 724 | if ent: 725 | if hasattr(ent, 'renderer'): 726 | ent.renderer.width = newval 727 | if hasattr(ent, 'physics_renderer'): 728 | ent.physics_renderer.width = newval 729 | 730 | def imgHeightChanged(self, value): 731 | self.inputPreview.text = value 732 | newval = float(value) 733 | ent = self.selectedEntity 734 | if ent: 735 | if hasattr(ent, 'renderer'): 736 | ent.renderer.height = newval 737 | if hasattr(ent, 'physics_renderer'): 738 | ent.physics_renderer.height = newval 739 | def on_width_change(self, instance, value): 740 | self.inputPreview.text = instance.text 741 | space = self.gameref.space 742 | newrad = float(value) 743 | #if self.selectedItem and self.selectedEntity: 744 | #self.selectedItem.width = (newrad) 745 | #self.selectedEntity.physics_renderer.width = newrad 746 | #space.reindex_shape(self.selectedItem) 747 | #print dir(self.selectedEntity) 748 | #print dir(self.selectedEntity.physics) 749 | #print self.selectedItem.cache_bb() 750 | shape = self.selectedItem 751 | if shape and self.selectedEntity: 752 | newshape = cy.BoxShape(shape.body, newrad, shape.height) 753 | newshape.body.moment = cy.moment_for_box(newshape.body.mass, newrad, shape.height) 754 | newshape.collision_type = shape.collision_type 755 | newshape.elasticity = shape.elasticity 756 | newshape.friction = shape.friction 757 | space.add_shape(newshape) 758 | space.remove(shape) 759 | #print shape.body._shapes 760 | self.selectedEntity.physics.shapes = [newshape] 761 | self.selectedItem = newshape 762 | self.selectedEntity.physics_renderer.width = newrad 763 | 764 | def on_height_change(self, instance, value): 765 | self.inputPreview.text = instance.text 766 | space = self.gameref.space 767 | newrad = float(value) 768 | shape = self.selectedItem 769 | if shape and self.selectedEntity: 770 | newshape = cy.BoxShape(shape.body, shape.width, newrad) 771 | newshape.body.moment = cy.moment_for_box(newshape.body.mass, newrad, shape.height) 772 | newshape.collision_type = shape.collision_type 773 | newshape.elasticity = shape.elasticity 774 | newshape.friction = shape.friction 775 | space.add_shape(newshape) 776 | space.remove(self.selectedItem) 777 | self.selectedEntity.physics.shapes = [newshape] 778 | self.selectedItem = newshape 779 | self.selectedEntity.physics_renderer.height = newrad 780 | def scale_cpoints(self,ns): 781 | 782 | for id in self.cpointids: 783 | e = self.gameref.gameworld.entities[id] 784 | nsz = 25*math.sqrt(ns) 785 | e.renderer.width=nsz 786 | e.renderer.height=nsz 787 | def setEnt(self, ent, fshape=None): 788 | self.fireText = False 789 | self.selectedItem = None 790 | self.selectedMenu.selectedLabel.text = "None" 791 | self.selectedEntity = ent 792 | self.inputPreview.text = "" 793 | self.selectedMenu.shapeInfo.clear_widgets() 794 | for id in self.cpointids: 795 | self.gameref.delObj(id) 796 | self.cpointids = [] 797 | if ent: 798 | self.selectedEntity = ent#self.gameref.gameworld.entities[shape.body.data] 799 | #ent = self.selectedEntity 800 | if hasattr(ent, 'splineshape'): 801 | 802 | for p in ent.splineshape.ControlPoints: 803 | ns = self.gameref.get_cam_scale() 804 | nsz = 25*math.sqrt(ns) 805 | pid = self.gameref.create_decoration(pos=(p[0], p[1]), width=nsz, height=nsz, 806 | texture='plank') 807 | self.cpointids.append(pid) 808 | #print dir(ent.physics) 809 | if hasattr(ent, 'physics'): 810 | if fshape: 811 | shape=fshape 812 | else: 813 | shape = self.selectedEntity.physics.shapes[0] 814 | self.selectedItem = shape 815 | self.selectedMenu.selectedLabel.text = ent.physics.shape_type+" "+str(shape.body.data) 816 | #tv = "x=%f\ny=%f" % (shape.body.position.x, shape.body.position.y) 817 | #self.selectedMenu.posLabel.text = tv 818 | self.selectedMenu.frictionLabel.text = "%0.2f" % shape.friction 819 | self.selectedMenu.massLabel.text = "%0.2f" % shape.body.mass 820 | self.selectedMenu.elasLabel.text = "%0.2f" % shape.elasticity 821 | #print self.gameref.scripty.collision_types[shape.collision_type], shape.collision_type 822 | self.selectedMenu.colTypeSpinner.text = self.gameref.scripty.collision_types[shape.collision_type] 823 | 824 | if shape.__class__.__name__ == "Circle": 825 | cs = CircleSettings() 826 | cs.radiusLabel.text = "%0.2f" % shape.radius 827 | cs.radiusLabel.bind(text=self.on_rad_change) 828 | self.selectedMenu.shapeInfo.add_widget(cs) 829 | elif shape.__class__.__name__ == "BoxShape": 830 | bs = BoxSettings() 831 | bs.widthLabel.text = "%0.2f" % shape.width 832 | bs.heightLabel.text = "%0.2f" % shape.height 833 | bs.widthLabel.bind(text=self.on_width_change) 834 | bs.heightLabel.bind(text=self.on_height_change) 835 | self.selectedMenu.shapeInfo.add_widget(bs) 836 | #elif shape.__class__.__name__ == "Poly": 837 | 838 | if self.gameref.selectedShapeID != None: 839 | self.gameref.delObj(self.gameref.selectedShapeID) 840 | self.gameref.selectedShapeID = None 841 | if ent: 842 | if self.selectedMenuView not in self.rightMenu.children: 843 | self.rightMenu.add_widget(self.selectedMenuView) 844 | if hasattr(ent, 'physics_renderer'): 845 | self.selectedMenu.texLabel.text = ent.physics_renderer.texture 846 | self.selectedMenu.imgWidthLabel.text = str(ent.physics_renderer.width) 847 | self.selectedMenu.imgHeightLabel.text = str(ent.physics_renderer.height) 848 | print "width=",ent.physics_renderer.width 849 | if hasattr(ent, 'poly_renderer'): 850 | texname = ent.poly_renderer.texture.split('/')[-1][:-4] 851 | self.selectedMenu.texLabel.text = texname 852 | ps = Button(text="simplify", on_press=self.simplifyPolyPressed) 853 | self.selectedMenu.shapeInfo.add_widget(ps) 854 | if hasattr(ent, 'color'): 855 | self.selectedMenu.redLabel.text = str(ent.color.r) 856 | self.selectedMenu.greenLabel.text = str(ent.color.g) 857 | self.selectedMenu.blueLabel.text = str(ent.color.b) 858 | self.selectedMenu.opacityLabel.text = str(ent.color.a) 859 | r = None 860 | if hasattr(ent,"physics_renderer"): 861 | self.gameref.selectedShapeID = self.gameref.create_decoration(pos=(shape.body.position.x, shape.body.position.y), 862 | width=ent.physics_renderer.width*1.1+10, height=ent.physics_renderer.height*1.1+10, 863 | texture='emptybox') 864 | else: 865 | self.rightMenu.remove_widget(self.selectedMenuView) 866 | self.fireText = True 867 | def setShape(self, shape): 868 | if shape: 869 | ent = self.gameref.gameworld.entities[shape.body.data] 870 | if ent: 871 | self.setEnt(ent,shape) 872 | else: 873 | self.setEnt(None) 874 | 875 | def delSelPressed(self, instance): 876 | if self.selectedEntity and self.gameref: 877 | self.gameref.delObj(self.selectedEntity.entity_id) 878 | self.setEnt(None) 879 | #if self.selectedItem and self.gameref: 880 | # self.gameref.delObj(self.selectedItem.body.data) 881 | # self.setShape(None) 882 | def sensorPressed(self, instance): 883 | if self.selectedItem and self.gameref: 884 | newval = not self.selectedItem.sensor 885 | self.selectedItem.sensor = newval 886 | instance.pressed = newval 887 | def scriptsPressed(self, instance): 888 | #if self.selectedItem and self.gameref: 889 | # 890 | # newval = self.gameref.scripty.collision_types[self.selectedItem.collision_type] 891 | if self.selectedItem: 892 | self.callbacksBox.setTypeA(self.gameref.scripty.collision_types[self.selectedItem.collision_type]) 893 | self.cbpopup.open() 894 | self.gameref.touches = {} 895 | def downloadsPressed(self, instance): 896 | self.dlpopup.open() 897 | self.downloadsBox.listLevels() 898 | self.gameref.touches = {} 899 | def saveThumb(self): 900 | filename = self.nameBox.text+".png" 901 | self.gameref.export_to_png(filename=filename) 902 | baseWidth = 150 903 | from PIL import Image 904 | # Open the image file. 905 | img = Image.open(filename) 906 | 907 | # Calculate the height using the same aspect ratio 908 | widthPercent = (baseWidth / float(img.size[0])) 909 | height = int((float(img.size[1]) * float(widthPercent))) 910 | 911 | # Resize it. 912 | img = img.resize((baseWidth, height), Image.BILINEAR) 913 | 914 | # Save it back to disk. 915 | img.save(filename) 916 | 917 | def saveAsPressed(self, instance): 918 | #self.saveThumb() 919 | self.sapopup.open() 920 | self.saveasBox.initUI() 921 | self.gameref.touches = {} 922 | def uploadPressed(self, instance): 923 | self.saveThumb() 924 | self.ulpopup.open() 925 | self.uploadBox.initUI() 926 | self.gameref.touches = {} 927 | 928 | def makeEntDataDict(self, ent): 929 | #create entity datadict if missing 930 | if not hasattr(ent, "datadict"): 931 | print "initing datadict" 932 | ent.datadict = {} 933 | 934 | def putDefaultsInDataDict(self): 935 | if self.selectedEntity == None:return 936 | self.makeEntDataDict(self.selectedEntity) 937 | scripty = self.gameref.scripty 938 | colfuncs = scripty.getHandlersForType(self.selectedItem.collision_type) 939 | print "defaults=",scripty.defaults 940 | print "colfuncs=",colfuncs 941 | for cf in colfuncs: 942 | if cf in scripty.defaults: 943 | #if len(colfunc)>0 and colfunc[0] in scripty.defaults: 944 | defaultdict = scripty.defaults[cf] 945 | print "defaultdict=",defaultdict 946 | for i in defaultdict.keys(): 947 | if i not in self.selectedEntity.datadict: 948 | self.selectedEntity.datadict[i] = defaultdict[i] 949 | 950 | def varsPressed(self, btn): 951 | if self.selectedEntity: 952 | self.putDefaultsInDataDict() 953 | Popup(title="Entity Varables", 954 | content=entDataBox(ddict = self.selectedEntity.datadict), 955 | size_hint=(0.8, 0.8), 956 | on_dismiss=None).open() 957 | def colTypeChanged(self, instance): 958 | if self.selectedEntity and self.gameref: 959 | newval = self.gameref.scripty.collision_types[instance.text] 960 | for shape in self.selectedEntity.physics.shapes: 961 | shape.collision_type = newval 962 | #self.gameref.reindexEnt(self.selectedEntity) 963 | print newval, instance.text 964 | self.putDefaultsInDataDict() 965 | 966 | 967 | def clearl2(self): 968 | self.clearl3() 969 | for i in self.l2menus: 970 | if i in self.leftMenu.children: 971 | self.leftMenu.remove_widget(i) 972 | def clearl3(self): 973 | for i in self.l3menus: 974 | if i in self.leftMenu.children: 975 | self.leftMenu.remove_widget(i) 976 | 977 | def changel2menu(self, newMenu): 978 | if newMenu in self.leftMenu.children: 979 | self.clearl2() 980 | self.leftMenu.size_hint_x = .1 981 | else: 982 | self.clearl2() 983 | self.leftMenu.add_widget(newMenu) 984 | self.leftMenu.size_hint_x = .2 985 | 986 | def changel3menu(self, newMenu): 987 | if newMenu in self.leftMenu.children: 988 | self.clearl3() 989 | self.leftMenu.size_hint_x = .2 990 | else: 991 | self.clearl3() 992 | self.leftMenu.add_widget(newMenu) 993 | self.leftMenu.size_hint_x = .3 994 | 995 | 996 | def massPressed(self, instance): 997 | self.massSlider.value = 0 if self.massSlider.value > 0 else 10 998 | 999 | 1000 | def playPressed(self, instance=None): 1001 | self.paused = not self.paused 1002 | if instance: instance.text = "Resume" if self.paused else "Pause" 1003 | def momemPressed(self, instance=None): 1004 | self.grav_backup, self.gameref.space.gravity = self.gameref.space.gravity, self.grav_backup 1005 | self.killMomem = not self.killMomem 1006 | if self.killMomem:self.leftMenu.momemButton.state = 'down' 1007 | else: self.leftMenu.momemButton.state = 'normal' 1008 | #instance.text = "Resume" if self.paused else "Pause" -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | from random import random 3 | from math import radians 4 | import json 5 | import os 6 | import cymunk as cy 7 | from math import * 8 | import os 9 | import PolyGen 10 | import Spline 11 | 12 | #import cProfile 13 | 14 | __version__ = "0.1" 15 | 16 | import serialisation 17 | from objscripts import ObjScripts 18 | 19 | from kivy.app import App 20 | from kivy.uix.widget import Widget 21 | from kivy.clock import Clock 22 | from kivy.core.window import Window 23 | import kivent 24 | from kivy.graphics import * 25 | from kivy.atlas import Atlas 26 | 27 | 28 | from kivy.utils import platform 29 | from os.path import dirname, join, exists, sep, expanduser, isfile 30 | 31 | import ui_elements 32 | 33 | import sys 34 | #import debugprint 35 | 36 | 37 | def cross(a, b): 38 | return a[0]*b[1]-a[1]*b[0] 39 | 40 | def cross3d(a, b): 41 | c = [a[1]*0 - 0*b[1], 42 | 0*b[0] - a[0]*0, 43 | a[0]*b[1] - a[1]*b[0]] 44 | return c 45 | 46 | class TestGame(Widget): 47 | def __init__(self, **kwargs): 48 | self.dataDir = "" 49 | super(TestGame, self).__init__(**kwargs) 50 | Clock.schedule_once(self.init_game) 51 | self.entIDs = [] 52 | self.mainTools = self.ids['gamescreenmanager'].ids['main_screen'].ids['mainTools'] 53 | self.mainTools.setRef(self) 54 | self.mainTools.setTool("poly") 55 | self.startID = -1 56 | self.finishID = -1 57 | self.selectedShapeID = None 58 | self.space = None 59 | self.serials = None 60 | self.scripty = None 61 | self.todelete = [] 62 | self.jointEnts = {} 63 | self.selectedListIndex = 0 64 | self.lastlist = None 65 | self.touches = {}#0: {"active": False, "pos": (0, 0), "screenpos": (0, 0)}} 66 | self.atlas = Atlas('assets/myatlas.atlas') 67 | try: 68 | self._keyboard = Window.request_keyboard(self._keyboard_closed, self) 69 | self._keyboard.bind(on_key_down=self._on_keyboard_down) 70 | except: 71 | print 'Python python no keyboard' 72 | 73 | size = Window.size 74 | with self.canvas.before: 75 | #Color(0.5, 0.65, 0.95) 76 | self.bgrect = Rectangle(source='sprites/bgm.jpg', size=size) 77 | Window.bind(on_resize=self.redogb) 78 | def redogb(self, a,b,c): 79 | size = Window.size 80 | self.bgrect.size =Window.size 81 | 82 | def init_game(self, dt): 83 | if platform == 'android' or True:#apply kovaks hack to android only 84 | try: 85 | self._init_game(0) 86 | except KeyError as err: 87 | print 'failed: rescheduling init' 88 | print err 89 | Clock.schedule_once(self.init_game) 90 | else: 91 | self._init_game(0) 92 | 93 | 94 | 95 | def _init_game(self, dt): 96 | self.setup_map() 97 | self.setup_states() 98 | self.set_state() 99 | Clock.schedule_once(self.__init_game) 100 | def __init_game(self, dt): 101 | self.space = self.gameworld.systems['physics'].space 102 | #self.space.add_collision_handler(1, 0, begin = self.pull2_first) 103 | self.serials = serialisation.Serials(self) 104 | self.scripty = ObjScripts(self) 105 | 106 | noload = True 107 | fileNamePath = self.dataDir+"settings.jso" 108 | if os.path.exists(self.dataDir+"settings.jso"): 109 | print "settings.jso found, loading last level" 110 | if os.path.isfile(fileNamePath): 111 | with open(fileNamePath) as fo: 112 | try: 113 | settingsDict = json.load(fo) 114 | self.serials.loadJSON(settingsDict['lastSave']) 115 | self.mainTools.nameBox.text = settingsDict['lastSave'][0:-5] 116 | noload = False 117 | except: 118 | print "could not load settings.jso level" 119 | import traceback 120 | traceback.print_exc() 121 | 122 | if noload: 123 | self.draw_some_stuff() 124 | print "level not loaded - making some stuff" 125 | 126 | Clock.schedule_interval(self.update, 0) 127 | Clock.schedule_once(self.init_sprites) 128 | 129 | def init_sprites(self, dt): 130 | #self.gameworld.systems['renderer'].do_rotate = True 131 | #self.gameworld.systems['renderer'].on_do_rotate(None,None) 132 | usprites = self.gameworld.systems['renderer'].uv_dict.keys() 133 | sprites = [] 134 | for k in usprites: 135 | if k != 'atlas_size' and k != 'main_texture': sprites.append(str(k)) 136 | self.mainTools.sprite_list = sprites 137 | 138 | def reindexEntID(self, entityID): 139 | self.reindexEnt(self.gameworld.entities[entityID]) 140 | 141 | def reindexEnt(self, entity): 142 | space = self.space 143 | if entity and hasattr(entity, "physics"): 144 | for s in entity.physics.shapes: 145 | space.reindex_shape(s) 146 | 147 | def draw_some_stuff(self): 148 | size = Window.size 149 | for x in range(50): 150 | pos = (randint(size[0] / 3, size[0]), randint(0, size[1])) 151 | self.create_circle(pos, y_vel=random() * -20, texture="sheep", radius=15, selectNow=False) 152 | self.create_box((size[0] / 2.0, 0), mass=0, width=size[0] * 2, height=10, angle=0, selectNow=False) 153 | self.create_circle((size[0] / 2.0,size[1] / 2.0), y_vel=random() * -20, texture="magicball", radius=150, mass=0, selectNow=False, collision_type=1, color=(1,1,1,0.4)) 154 | 155 | def _keyboard_closed(self): 156 | try: 157 | self._keyboard.unbind(on_key_down=self._on_keyboard_down) 158 | self._keyboard = None 159 | except: 160 | print "still no keyboard!" 161 | 162 | def _on_keyboard_down(self, keyboard, keycode, text, modifiers): 163 | space = self.space 164 | if keycode[1] == 'up': 165 | space.gravity = space.gravity.x, space.gravity.y + 10 166 | if keycode[1] == 'down': 167 | space.gravity = space.gravity.x, space.gravity.y - 10 168 | if keycode[1] == 'left': 169 | space.gravity = space.gravity.x - 10, space.gravity.y 170 | if keycode[1] == 'right': 171 | space.gravity = space.gravity.x + 10, space.gravity.y 172 | self.mainTools.inputPreview.text = str(self.space.gravity) 173 | return True 174 | 175 | def setGrav(self, g): 176 | self.space.gravity = (g[0], g[1]) 177 | self.mainTools.gravxSlider.value = g[0] 178 | self.mainTools.gravySlider.value = g[1] 179 | self.mainTools.inputPreview.text = str(self.space.gravity) 180 | def create_decoration(self, pos=(0, 0), width=40, height=40, angle=0, texture="sheep", color=(1,1,1,1)): 181 | create_component_dict = { 182 | 'renderer': {'texture': texture, 'size': (width, height)}, 183 | 'position': pos, 'rotate': angle ,'color':color} 184 | component_order = ['color', 'position', 'rotate', 'renderer'] 185 | entityID = self.gameworld.init_entity(create_component_dict, component_order) 186 | return entityID 187 | 188 | def create_circle(self, pos, radius=6., mass=10., friction=1.0, elasticity=.5, angle=.0, x_vel=.0, y_vel=.0, 189 | angular_velocity=0., texture="sheep", selectNow=True, sensor = False, collision_type = 0, 190 | color=(1,1,1,1), do_physics = True): 191 | shape_dict = {'inner_radius': 0, 'outer_radius': radius, 192 | 'mass': mass, 'offset': (0, 0)} 193 | col_shape = {'shape_type': 'circle', 'elasticity': elasticity, 194 | 'collision_type': collision_type, 'shape_info': shape_dict, 'friction': friction} 195 | col_shapes = [col_shape] 196 | physics_component = {'main_shape': 'circle', 197 | 'velocity': (x_vel, y_vel), 198 | 'position': pos, 'angle': angle, 199 | 'angular_velocity': angular_velocity, 200 | 'vel_limit': 2048, 201 | 'ang_vel_limit': radians(2000), 202 | 'mass': mass, 'col_shapes': col_shapes} 203 | create_component_dict = {#'physics': physics_component, 204 | 'color':color, 205 | 'position': pos, 'rotate': angle} 206 | #component_order = ['color', 'position', 'rotate', 207 | # 'physics_renderer'] 208 | render_component = {'texture': texture, 'size': (radius * 2, radius * 2)} 209 | if do_physics: 210 | create_component_dict['physics'] = physics_component 211 | create_component_dict['physics_renderer'] = render_component 212 | component_order = ['color', 'position', 'rotate', 213 | 'physics', 'physics_renderer'] 214 | else: 215 | create_component_dict['renderer'] = render_component 216 | component_order = ['color', 'position', 'rotate', 217 | 'renderer'] 218 | return self.create_ent_from_dict(create_component_dict, component_order, selectNow) 219 | def getEntFromID(self, entID): 220 | return self.gameworld.entities[entID] 221 | def create_ent_from_dict(self,create_component_dict, component_order, selectNow = True): 222 | entityID = self.gameworld.init_entity(create_component_dict, component_order) 223 | self.entIDs.append(entityID) 224 | if self.mainTools.paused: (self.gameworld.systems['physics'].update(0.00001)) 225 | #if selectNow: self.mainTools.setShape(self.gameworld.entities[entityID].physics.shapes[0]) 226 | if selectNow: 227 | do_physics = 'physics' in component_order 228 | if do_physics: 229 | self.mainTools.setShape(self.gameworld.entities[entityID].physics.shapes[0]) 230 | else: 231 | self.mainTools.setEnt(self.gameworld.entities[entityID]) 232 | return entityID 233 | def create_segment(self, pos, width=40., height=40., mass=10., friction=1.0, elasticity=.5, angle=.0, x_vel=.0, y_vel=.0, 234 | angular_velocity=.0, texture="face_box", selectNow=True, sensor = False, collision_type = 0, color=(1,1,1,1)): 235 | a= cy.Vec2d(-width/2,0) 236 | b= cy.Vec2d(width/2,0) 237 | box_dict = { 238 | 'a': a, 239 | 'b': b, 240 | 'radius': height, 241 | 'mass': mass} 242 | col_shape = {'shape_type': 'segment', 'elasticity': elasticity, 243 | 'collision_type': collision_type, 'shape_info': box_dict, 'friction': friction} 244 | col_shapes = [col_shape] 245 | physics_component = {'main_shape': 'box', 246 | 'velocity': (x_vel, y_vel), 247 | 'position': pos, 'angle': angle, 248 | 'angular_velocity': angular_velocity, 249 | 'vel_limit': 2048, 250 | 'ang_vel_limit': radians(2000), 251 | 'mass': mass, 'col_shapes': col_shapes} 252 | create_component_dict = {'physics': physics_component, 253 | 'physics_renderer': {'texture': texture, 'size': (width, height)}, 'color':color, 254 | 'position': pos, 'rotate': angle} 255 | component_order = ['color', 'position', 'rotate', 256 | 'physics', 'physics_renderer'] 257 | return self.create_ent_from_dict(create_component_dict, component_order, selectNow) 258 | def create_box(self, pos, width=40., height=40., mass=10., friction=1.0, elasticity=.5, angle=.0, x_vel=.0, y_vel=.0, 259 | angular_velocity=.0, texture="face_box", selectNow=True, sensor = False, collision_type = 0, 260 | color=(1,1,1,1), do_physics = True): 261 | box_dict = { 262 | 'width': width, 263 | 'height': height, 264 | 'mass': mass} 265 | col_shape = {'shape_type': 'box', 'elasticity': elasticity, 266 | 'collision_type': collision_type, 'shape_info': box_dict, 'friction': friction} 267 | col_shapes = [col_shape] 268 | physics_component = {'main_shape': 'box', 269 | 'velocity': (x_vel, y_vel), 270 | 'position': pos, 'angle': angle, 271 | 'angular_velocity': angular_velocity, 272 | 'vel_limit': 2048, 273 | 'ang_vel_limit': radians(2000), 274 | 'mass': mass, 'col_shapes': col_shapes} 275 | create_component_dict = {'color':color, 276 | 'position': pos, 'rotate': angle} 277 | #component_order = ['color', 'position', 'rotate', 278 | # 'physics', 'physics_renderer'] 279 | 280 | render_component = {'texture': texture, 'size': (width, height)} 281 | if do_physics: 282 | create_component_dict['physics'] = physics_component 283 | create_component_dict['physics_renderer'] = render_component 284 | component_order = ['color', 'position', 'rotate', 285 | 'physics', 'physics_renderer'] 286 | else: 287 | create_component_dict['renderer'] = render_component 288 | component_order = ['color', 'position', 'rotate', 289 | 'renderer'] 290 | return self.create_ent_from_dict(create_component_dict, component_order, selectNow) 291 | def create_sprite(self, pos, shape_type='box', radius=40., width=None, height=None, mass=10., friction=None, elasticity=None, angle=.0, x_vel=.0, y_vel=.0, 292 | angular_velocity=.0, texture=None, selectNow=True, sensor = False, collision_type = 0, 293 | color=None, do_physics = True, old_shape_id=None, old_shape=None, replace_old_shape=False): 294 | if width == None:width=radius*2 295 | if height == None:height=radius*2 296 | print "color=",color 297 | print "old_shape_id=",old_shape_id 298 | print "old_shape=",old_shape 299 | if old_shape_id:old_shape = self.getEntFromID(old_shape_id) 300 | print "old_shape=",old_shape 301 | if old_shape: 302 | 303 | if do_physics == None: 304 | #print oldpoly.load_order 305 | do_physics = 'physics' in old_shape.load_order 306 | #print do_physics 307 | if hasattr(old_shape, 'physics'): 308 | if friction == None: friction = old_shape.physics.shapes[0].friction 309 | if elasticity == None: elasticity = old_shape.physics.shapes[0].elasticity 310 | if color == None: 311 | c = old_shape.color 312 | print "c=",c 313 | color = (c.r, c.g ,c.b ,c.a) 314 | print color 315 | if texture == None: 316 | texture = old_shape.poly_renderer.texture 317 | if replace_old_shape: self.delObj(old_shape_id) 318 | if friction == None: friction = 1.0 319 | if elasticity == None: elasticity = .5 320 | if color == None:color = (1,1,1,1) 321 | print color 322 | if texture == None: texture = "snow" 323 | if do_physics == None: do_physics = True 324 | 325 | if shape_type == 'box': 326 | shape_dict = { 327 | 'width': width, 328 | 'height': height, 329 | 'mass': mass} 330 | elif shape_type == 'circle': 331 | shape_dict = {'inner_radius': 0, 'outer_radius': radius, 332 | 'mass': mass, 'offset': (0, 0)} 333 | col_shape = {'shape_type': shape_type, 'elasticity': elasticity, 334 | 'collision_type': collision_type, 'shape_info': shape_dict, 'friction': friction} 335 | col_shapes = [col_shape] 336 | physics_component = {'main_shape': shape_type, 337 | 'velocity': (x_vel, y_vel), 338 | 'position': pos, 'angle': angle, 339 | 'angular_velocity': angular_velocity, 340 | 'vel_limit': 2048, 341 | 'ang_vel_limit': radians(2000), 342 | 'mass': mass, 'col_shapes': col_shapes} 343 | create_component_dict = {'color':color, 344 | 'position': pos, 'rotate': angle} 345 | #component_order = ['color', 'position', 'rotate', 346 | # 'physics', 'physics_renderer'] 347 | 348 | render_component = {'texture': texture, 'size': (width, height)} 349 | if do_physics: 350 | create_component_dict['physics'] = physics_component 351 | create_component_dict['physics_renderer'] = render_component 352 | component_order = ['color', 'position', 'rotate', 353 | 'physics', 'physics_renderer'] 354 | else: 355 | create_component_dict['renderer'] = render_component 356 | component_order = ['color', 'position', 'rotate', 357 | 'renderer'] 358 | return self.create_ent_from_dict(create_component_dict, component_order, selectNow) 359 | def create_spline(self, pos, spline, lastpolyid=None, mass=0., friction=None, elasticity=None, angle=.0, x_vel=.0, y_vel=.0, 360 | angular_velocity=.0, texture=None, selectNow=True, do_physics = None, collision_type = 0, color=None): 361 | pg = PolyGen.PolyGen() 362 | spline.DrawCurve() 363 | pg.from_spline(spline.subpoints) 364 | #do_physics = self.mainTools.polyMenu.polyPhysButton.state != 'down' 365 | spline_ent_id = self.create_poly(pos,pg,lastpolyid=lastpolyid, mass=mass, friction=friction, 366 | elasticity=elasticity, angle=angle, x_vel=x_vel, y_vel=y_vel, 367 | angular_velocity=angular_velocity, texture=texture, selectNow=selectNow, 368 | do_physics = do_physics, collision_type = collision_type, color=color)#, do_physics=do_physics) 369 | 370 | spline_ent = self.getEntFromID(spline_ent_id) 371 | spline_ent.splineshape = spline 372 | print "made spline" 373 | return spline_ent_id 374 | def create_poly(self, pos, polygon, lastpolyid=None, mass=0., friction=None, elasticity=None, angle=.0, x_vel=.0, y_vel=.0, 375 | angular_velocity=.0, texture=None, selectNow=True, do_physics = None, collision_type = 0, color=None): 376 | #print "poly, oldpoly=", lastpolyid 377 | 378 | #print do_physics 379 | if lastpolyid: 380 | oldpoly = self.getEntFromID(lastpolyid) 381 | if do_physics == None: 382 | #print oldpoly.load_order 383 | do_physics = 'physics' in oldpoly.load_order 384 | #print do_physics 385 | if hasattr(oldpoly, 'physics'): 386 | if friction == None: friction = oldpoly.physics.shapes[0].friction 387 | if elasticity == None: elasticity = oldpoly.physics.shapes[0].elasticity 388 | if color == None: 389 | c = oldpoly.color 390 | color = (c.r, c.g ,c.b ,c.a) 391 | if texture == None: 392 | texture = oldpoly.poly_renderer.texture 393 | self.delObj(lastpolyid) 394 | if friction == None: friction = 1.0 395 | if elasticity == None: elasticity = .5 396 | if color == None:color = (1,1,1,1) 397 | if texture == None: texture = "snow" 398 | if do_physics == None: do_physics = True 399 | 400 | pg = polygon 401 | 402 | 403 | pg.color = color 404 | create_dict = pg.draw_from_Polygon() 405 | if create_dict == False:return 406 | create_dict['do_texture'] = True 407 | if texture[-4:] != '.png': texture = 'sprites/'+texture+'.png' 408 | create_dict['texture'] = texture 409 | print texture 410 | 411 | triangles = create_dict['triangles'] 412 | tricount = len(triangles) 413 | if tricount < 1: return 414 | submass = mass/tricount 415 | verts = create_dict['vertices'] 416 | #print "bothtest, cptest, crtest, failtest" 417 | #print bothtest, cptest, crtest, failtest 418 | #print remlist #TODO really, this should be empty! 419 | 420 | col_shapes, remlist = PolyGen.col_shapes_from_tris(triangles,verts,submass,elasticity,collision_type,friction) 421 | 422 | remlist.reverse() 423 | for r in remlist: 424 | triangles.remove(triangles[r]) 425 | create_dict['tri_count'] = len(triangles) 426 | if len(col_shapes) == 0:return None 427 | physics_component = {'main_shape': 'poly', 428 | 'velocity': (x_vel, y_vel), 429 | 'position': (0,0), 'angle': angle, 430 | 'angular_velocity': angular_velocity, 431 | 'vel_limit': 2048, 432 | 'ang_vel_limit': radians(2000), 433 | 'mass': mass, 'col_shapes': col_shapes} 434 | 435 | create_component_dict = {'color':color, 436 | 'position': pos, 'rotate': 0, 'poly_renderer': create_dict} 437 | component_order = ['color', 'position', 'rotate', 'poly_renderer'] 438 | if do_physics: 439 | create_component_dict['physics'] = physics_component 440 | component_order = ['color', 'position', 'rotate', 'physics', 'poly_renderer'] 441 | if lastpolyid and 0: 442 | poly = self.getEntFromID(lastpolyid) 443 | 444 | poly.poly_renderer.vert_mesh.load_from_python(verts, triangles)#, create_dict['vert_count'], create_dict['tri_count']) 445 | self.gameworld.systems['poly_renderer'].redraw_entity(lastpolyid) 446 | return lastpolyid 447 | else: 448 | #print "col_shapes=",col_shapes 449 | newpolyID = self.gameworld.init_entity(create_component_dict, component_order) 450 | self.entIDs.append(newpolyID) 451 | newpoly = self.getEntFromID(newpolyID) 452 | newpoly.polyshape = pg 453 | 454 | print newpoly.poly_renderer.texture 455 | #if not do_physics: 456 | # newpoly.load_order.remove('physics') 457 | #newpoly.load_order = ['color', 'position', 'rotate', 'poly_renderer'] 458 | if selectNow: self.mainTools.setEnt(self.gameworld.entities[newpolyID]) 459 | 460 | #newpoly.poly_renderer.texture.wrap = 'repeat' 461 | 462 | #print "poly has: " + str(len(triangles)) + " triangles" 463 | return newpolyID 464 | def setup_map(self): 465 | gameworld = self.gameworld 466 | gameworld.currentmap = gameworld.systems['map'] 467 | def getShapeAt(self, x,y): 468 | position = cy.Vec2d(x,y) 469 | return self.space.point_query_first(position) 470 | def getShapesAt(self, x,y): 471 | return self.getShapesAtVec(cy.Vec2d(x,y)) 472 | def getShapesAtVec(self, position): 473 | b = cy.Body() 474 | b.position = position 475 | shapeos = cy.Circle(b, 1) 476 | return self.space.shape_query(shapeos) 477 | def setEntIDPosSizeRot(self, entID, x,y,w,h,r=0): 478 | self.setEntPosSizeRot(self.gameworld.entities[entID], x,y,w,h,r) 479 | def setEntPosSizeRot(self, ent, x,y,w,h,r=0): 480 | ent.position.x = x 481 | ent.position.y = y 482 | ent.renderer.width = w 483 | ent.renderer.height = h 484 | ent.rotate.r = r 485 | def deleteJoint(self, j): 486 | if j in self.space.constraints: 487 | #print "removing ",j, " from space" 488 | #print "constraints before joint removal: ", self.space.constraints 489 | #print j.a, j.b 490 | self.space.remove(j) 491 | #print "constraints after joint removal: ", self.space.constraints 492 | #if j in self.space.constraints: 493 | # self.space.constraints.remove(j) 494 | if j in self.jointEnts: 495 | #print "removing ent and from dict" 496 | jent = self.jointEnts[j] 497 | eid = jent.entity_id 498 | self.gameworld.remove_entity(eid) 499 | #if self.selectedShapeID == eid: self.mainTools.setShape(None) 500 | del self.jointEnts[j] 501 | def create_joint(self, b1, b2, a1=(0, 0), a2=(0, 0), 502 | type='PivotJoint', **kwargs): 503 | if b1 is b2:return 504 | space = self.space 505 | qj = None 506 | if type == "PivotJoint": 507 | qj = cy.PivotJoint(b1, b2, a1, a2) 508 | space.add(qj) 509 | if type == "PinJoint": 510 | qj = cy.PinJoint(b1, b2, a1, a2) 511 | space.add(qj) 512 | if type == "DampedSpring": 513 | qj = cy.DampedSpring(b1, b2, a1,a2, kwargs['rest_length'], 514 | kwargs['stiffness'], 515 | kwargs['damping']) 516 | space.add(qj) 517 | 518 | jrid = self.create_decoration(pos=(b1.position.x, b1.position.y), width=20, height=20, 519 | texture='plank') 520 | jrent = self.getEntFromID(jrid) 521 | jrent.joint = qj 522 | #print dir(qj) 523 | #qj.entity_id = jrid 524 | self.jointEnts[qj] = jrent 525 | 526 | 527 | def on_touch_move(self, touch): 528 | if touch.id not in self.touches: return 529 | self.mainTools.on_touch_move(touch) 530 | space = self.space 531 | ctouch = self.touches[touch.id] 532 | 533 | if ctouch['onmenu']: return 534 | pos = self.getWorldPosFromTouch(touch) 535 | spos = ctouch['pos'] 536 | currentTool = ctouch['tool'] 537 | ctouch['newpos'] = pos 538 | ctouch['ownbody'].position = pos 539 | 540 | shape = self.getShapeAt(pos[0], pos[1]) 541 | ctouch['touchingnow'] = shape 542 | 543 | 544 | xd = spos[0] - pos[0] 545 | yd = spos[1] - pos[1] 546 | dist = sqrt(xd ** 2 + yd ** 2) 547 | midx = (spos[0] + pos[0]) / 2.0 548 | midy = (spos[1] + pos[1]) / 2.0 549 | angle = atan2(yd, xd) 550 | if currentTool == 'polysub': 551 | if dist > 10: 552 | polys = self.get_touching_polys(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 553 | for p in polys: 554 | pg = p.polyshape 555 | pg.sub_circle_polygon(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 556 | pg.sub_square_polygon((midx,midy),dist,self.mainTools.polyMenu.brushSizeSlider.value*1.96, angle) 557 | self.create_poly(pos,p.polyshape,p.entity_id) 558 | ctouch['pos'] = pos 559 | 560 | if 'polygen' in ctouch: 561 | pg = ctouch['polygen'] 562 | if dist > 10: 563 | 564 | pg.draw_circle_polygon(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 565 | pg.draw_square_polygon((midx,midy),dist,self.mainTools.polyMenu.brushSizeSlider.value*1.96, angle) 566 | #pg.draw_square_polygon(pos, 100, self.mainTools.polyMenu.brushSizeSlider.value*2) 567 | lpid=None 568 | if 'lastpolyid' in ctouch: 569 | lpid = ctouch['lastpolyid'] 570 | del ctouch['lastpolyid'] 571 | ctouch['lastpolyid'] = self.create_poly(pos,pg,lpid) 572 | ctouch['pos'] = pos 573 | 574 | if currentTool == 'splineed': 575 | ent = self.mainTools.selectedEntity 576 | if ent: 577 | if hasattr(ent, 'splineshape'): 578 | #print "entid=", ent.entity_id 579 | ss = ent.splineshape 580 | #ss.add_or_select(pos, 40) 581 | 582 | if ss.selected_point != None: 583 | ss.ControlPoints[ss.selected_point] = pos 584 | ss.DrawCurve() 585 | ent.polyshape.from_spline(ss.subpoints) 586 | #print ss.ControlPoints 587 | 588 | if ent.polyshape.poly.area()>10: 589 | spline_ent_id = self.create_poly(pos,ent.polyshape,ent.entity_id) 590 | #print "spline_ent_id=",spline_ent_id 591 | spline_ent = self.getEntFromID(spline_ent_id) 592 | spline_ent.splineshape = ss 593 | shape = spline_ent.physics.shapes[0] 594 | self.mainTools.setEnt(spline_ent) 595 | #self.create_poly((0,0),pg,ent.entity_id) 596 | 597 | if currentTool == "camera": 598 | #print len(self.touches) 599 | ccount=0 600 | viewport = self.gameworld.systems['gameview'] 601 | for t in self.touches: 602 | to = self.touches[t] 603 | if to['tool'] == 'camera': 604 | ccount+=1 605 | if ccount < 2: 606 | #super(TestGame, self).on_touch_move(touch) 607 | viewport.camera_pos[0]-=xd 608 | viewport.camera_pos[1]-=yd 609 | else: 610 | screen_mid = (viewport.size[0]*0.5,viewport.size[1]*0.5) 611 | #screen_mid = ctouch['screenpos'] 612 | sf = 1.0-touch.dy*0.001#+yd*0.00003 613 | #print touch.dy 614 | #camera_scale = viewport.camera_scale*sf+yd*0.0001 615 | #camera_scale = max(0.2, min(20, camera_scale)) 616 | self.zoomcam(sf, screen_mid) 617 | #print camera_scale 618 | #viewport.camera_scale=camera_scale 619 | 620 | 621 | if 'previewShape' in ctouch: 622 | psid = ctouch['previewShape'] 623 | #xd = spos[0] - pos[0] 624 | #yd = spos[1] - pos[1] 625 | #dist = sqrt(xd ** 2 + yd ** 2) 626 | 627 | if currentTool == "box": 628 | self.setEntIDPosSizeRot(psid, midx,midy,xd,yd) 629 | if currentTool == "circle": 630 | self.setEntIDPosSizeRot(psid, spos[0],spos[1],dist*2,dist*2,angle) 631 | if currentTool == "square": 632 | self.setEntIDPosSizeRot(psid, spos[0],spos[1],dist*2,dist*2,angle) 633 | if currentTool == "plank": 634 | self.setEntIDPosSizeRot(psid, midx,midy,dist,10, angle) 635 | if currentTool == "draw": 636 | self.setEntIDPosSizeRot(psid, midx,midy,dist,10, angle) 637 | if dist > 10: 638 | mass = self.mainTools.massSlider.value 639 | do_physics = self.mainTools.createMenu.spritePhysButton.state != 'down' 640 | self.create_box((midx, midy), mass=mass, width=dist, height=10, angle=angle, 641 | texture=self.mainTools.spriteSpinner.text, selectNow=False, 642 | do_physics=do_physics) 643 | ctouch['pos'] = pos 644 | 645 | shape = ctouch['touching'] 646 | if currentTool == 'rotate' and shape: 647 | cpos = (shape.body.position.x,shape.body.position.y) 648 | if shape.__class__.__name__ == 'Poly': 649 | bb = self.getEntFromID(shape.body.data).polyshape.poly.boundingBox() 650 | cpos = ((bb[0]+bb[1])/2,(bb[2]+bb[3])/2) 651 | xd = cpos[0] - pos[0] 652 | yd = cpos[1] - pos[1] 653 | angle = atan2(yd, xd) 654 | if 'origAngle' not in ctouch: ctouch['origAngle'] = shape.body.angle-angle 655 | shape.body.angle = (angle+ctouch['origAngle']) 656 | if (currentTool == 'drag' or currentTool == 'paste'): 657 | viewport = self.gameworld.systems['gameview'] 658 | if shape and (shape.body.is_static or self.mainTools.paused): 659 | dx = touch.dx*viewport.camera_scale 660 | dy = touch.dy*viewport.camera_scale 661 | shape.body.position = (shape.body.position.x + dx, shape.body.position.y + dy) 662 | self.reindexEntID(shape.body.data) 663 | if self.mainTools.paused: 664 | (self.gameworld.systems['physics'].update(0.00000001)) 665 | (self.gameworld.systems['physics_renderer'].update(0.00000001)) 666 | (self.gameworld.systems['renderer'].update(0.00000001)) 667 | #space.reindex_shape(shape) 668 | #else: 669 | # ent = self.mainTools.selectedEntity#TODO alter position component here 670 | 671 | 672 | def on_touch_up(self, touch): 673 | self.mainTools.on_touch_up(touch) 674 | if touch.id not in self.touches: 675 | print super(TestGame, self).on_touch_up(touch) 676 | print "touchdown not found, mousewheel?" 677 | return 678 | ctouch = self.touches[touch.id] 679 | del self.touches[touch.id] 680 | pos = self.getWorldPosFromTouch(touch) 681 | spos = ctouch['pos'] 682 | currentTool = ctouch['tool'] 683 | if 'previewShape' in ctouch: 684 | self.gameworld.remove_entity(ctouch['previewShape']) 685 | # self.canvas.before.remove(ctouch['previewShape']) 686 | 687 | space = self.space 688 | position = cy.Vec2d(pos[0], pos[1]) 689 | shape = space.point_query_first(position) 690 | ctouch['touchingnow'] = shape 691 | 692 | if 'mousejoint' in ctouch and (currentTool != "pin"): 693 | if ctouch['mousejoint'] in self.space.constraints: 694 | space.remove(ctouch['mousejoint']) 695 | 696 | if ctouch['onmenu']: return 697 | 698 | if 'polygen' in ctouch: 699 | pg = ctouch['polygen'] 700 | lpid=None 701 | if 'lastpolyid' in ctouch: 702 | lpid = ctouch['lastpolyid'] 703 | del ctouch['lastpolyid'] 704 | ctouch['lastpolyid'] = self.create_poly(pos,pg,lpid) 705 | 706 | tshape = ctouch['touching'] 707 | if tshape and shape: 708 | sposition = cy.Vec2d(spos[0], spos[1]) 709 | b1 = tshape.body 710 | b2 = shape.body 711 | b1l = b1.world_to_local(sposition) 712 | b2l = b2.world_to_local(position) 713 | if currentTool == 'c2p': 714 | self.create_joint(b1, b2, (0, 0), 715 | (b2l['x'], b2l['y']), "PinJoint") 716 | #qj = cy.PinJoint(b1, b2, (0, 0), 717 | # (b2l['x'], b2l['y'])) 718 | #space.add(qj) 719 | 720 | if currentTool == 'p2p': 721 | self.create_joint(b1, b2, (b1l['x'], b1l['y']), 722 | (b2l['x'], b2l['y']), "PinJoint") 723 | #space.add(qj) 724 | 725 | if currentTool == 'p2ps': 726 | dvec = cy.Vec2d(position.x - sposition.x, position.y - sposition.y) 727 | dist = sqrt(dvec.x ** 2 + dvec.y ** 2) 728 | self.create_joint(b1, b2, (b1l['x'], b1l['y']), (b2l['x'], b2l['y']), "DampedSpring" 729 | , rest_length=dist, stiffness=100, 730 | damping=0.1) 731 | #space.add(qj) kwargs['rest_length'], 732 | # kwargs['stiffness'], 733 | # kwargs['damping']) 734 | 735 | if currentTool == 'c2c': 736 | self.create_joint(b1, b2, (0, 0), 737 | (0, 0), "PinJoint") 738 | #b2.physics.shapes[0].group=1 739 | #b1.physics.shapes[0].group=1 740 | #space.add(qj) 741 | 742 | 743 | xd = spos[0] - pos[0] 744 | yd = spos[1] - pos[1] 745 | midx = (spos[0] + pos[0]) / 2.0 746 | midy = (spos[1] + pos[1]) / 2.0 747 | mass = self.mainTools.massSlider.value 748 | angle = atan2(yd, xd) 749 | dist = sqrt(xd ** 2 + yd ** 2) 750 | 751 | ent = self.mainTools.selectedEntity 752 | #print currentTool, tshape 753 | if currentTool == 'drag': 754 | if ent: 755 | if hasattr(ent, 'splineshape'): 756 | ss = ent.splineshape 757 | cps = ss.ControlPoints 758 | 759 | for pindex in range(len(cps)): 760 | p = cps[pindex] 761 | cps[pindex]=(p[0]-xd, p[1]-yd) 762 | #p[0]-=xd 763 | #p[1]-=yd 764 | self.create_spline((0,0),ss,ent.entity_id) 765 | elif hasattr(ent, 'polyshape'): 766 | pg = ent.polyshape 767 | pg.poly.shift(-xd,-yd) 768 | self.create_poly((0,0),pg,ent.entity_id) 769 | 770 | if currentTool == 'rotate' and tshape: 771 | ispoly = tshape.__class__.__name__ == 'Poly' 772 | if ispoly: 773 | bang = tshape.body.angle 774 | entID = tshape.body.data 775 | ent = self.getEntFromID(entID) 776 | pg = ent.polyshape 777 | pg.poly.rotate(bang) 778 | self.create_poly((0,0),pg,entID) 779 | 780 | 781 | do_physics = self.mainTools.createMenu.spritePhysButton.state != 'down' 782 | last_obj = None 783 | print "lastobj=",last_obj 784 | if self.mainTools.selectedEntity and self.mainTools.cloneSpriteButton.state == 'down': 785 | last_obj = self.mainTools.selectedEntity 786 | if (currentTool == "draw" or currentTool == "plank"): 787 | if dist < 4: dist = 8 788 | #self.create_box((midx, midy), mass=mass, width=dist, height=10, angle=angle, 789 | # texture=self.mainTools.spriteSpinner.text, do_physics=do_physics) 790 | self.create_sprite((midx, midy), mass=mass, width=dist, height=10, angle=angle, 791 | texture=self.mainTools.spriteSpinner.text, do_physics=do_physics, 792 | old_shape=last_obj) 793 | 794 | if currentTool == "start": 795 | if self.startID < 0: 796 | self.startID = self.create_circle(pos, mass=0, radius=30, texture="orb") 797 | else: 798 | ent = self.gameworld.entities[self.startID] 799 | ent.physics.body.position = pos 800 | self.reindexEnt(ent) 801 | if currentTool == "end": 802 | if self.finishID < 0: 803 | self.finishID = self.create_circle(pos, mass=0, radius=30, texture="checksphere") 804 | else: 805 | ent = self.gameworld.entities[self.finishID] 806 | ent.physics.body.position = pos 807 | self.reindexEnt(ent) 808 | 809 | if currentTool == "circle": 810 | if dist < 4: dist = 8 811 | #self.create_circle(spos, mass=mass, radius=dist, texture=self.mainTools.spriteSpinner.text, 812 | # angle=angle, do_physics=do_physics) 813 | self.create_sprite(spos, mass=mass, radius=dist, texture=self.mainTools.spriteSpinner.text, 814 | angle=angle, do_physics=do_physics, shape_type='circle', 815 | old_shape=last_obj) 816 | if currentTool == "box": 817 | width = fabs(xd) 818 | height = fabs(yd) 819 | if width< 4: width=8 820 | if height< 4: height=8 821 | self.create_sprite((midx, midy), mass=mass, width=width, height=height, angle=0, 822 | texture=self.mainTools.spriteSpinner.text, do_physics=do_physics, 823 | old_shape=last_obj) 824 | if currentTool == "square": 825 | if dist < 4: dist = 8 826 | self.create_sprite(spos, mass=mass, width=dist * 2, height=dist * 2, angle=angle, 827 | texture=self.mainTools.spriteSpinner.text, do_physics=do_physics, 828 | old_shape=last_obj) 829 | #self.touches[touch.id] = {"active": False, "newpos": pos, "screenpos": (touch.x, touch.y)} 830 | #del self.touches[touch.id] 831 | def get_cam_scale(self): 832 | viewport = self.gameworld.systems['gameview'] 833 | return viewport.camera_scale 834 | def zoomcam(self, sf, pos = (0,0)): 835 | pwp = self.getWorldPosFromTuple(pos) 836 | viewport = self.gameworld.systems['gameview'] 837 | camera_scale = viewport.camera_scale*sf 838 | camera_scale = max(0.2, min(20, camera_scale)) 839 | viewport.camera_scale=camera_scale 840 | pap = self.getWorldPosFromTuple(pos) 841 | diff = (pap[0]-pwp[0], pap[1]-pwp[1]) 842 | viewport.camera_pos[0]+=diff[0] 843 | viewport.camera_pos[1]+=diff[1] 844 | self.mainTools.scale_cpoints(camera_scale) 845 | def on_touch_down(self, touch): 846 | print "TOUCHDOWN\n" 847 | #print dir(touch) 848 | if hasattr(touch, 'button'): 849 | if touch.button == 'scrollup': 850 | self.zoomcam(1.02, (touch.x,touch.y)) 851 | return 852 | if touch.button == 'scrolldown': 853 | self.zoomcam(0.98, (touch.x,touch.y)) 854 | return 855 | 856 | pos = self.getWorldPosFromTouch(touch) 857 | position = cy.Vec2d(pos[0], pos[1]) 858 | space = self.space 859 | shape = None#space.point_query_first(position) 860 | shapes = self.getShapesAtVec(position) 861 | if len(shapes) >0: shape = shapes[0] 862 | if shapes==self.lastlist and shapes != []: 863 | self.selectedListIndex +=1 864 | if self.selectedListIndex == len(shapes):self.selectedListIndex=0 865 | shape=shapes[self.selectedListIndex] 866 | else: self.selectedListIndex =0 867 | self.lastlist = shapes 868 | #self.selectedShape = shape 869 | print "touched shape:", shape 870 | print "touched shapes:", shapes 871 | self.touches[touch.id] = {"active": False, "pos": pos, "newpos": pos, "screenpos": (touch.x, touch.y), 872 | "tool": self.mainTools.currentTool, "onmenu": False, "touching": shape, 873 | "touchingnow": shape, "ownbody": cy.Body()} 874 | ctouch = self.touches[touch.id] 875 | if self.mainTools.on_touch_down(touch): #True:#touch.x < self.width*.1: 876 | ctouch["onmenu"] = True 877 | #sresult = super(TestGame, self).on_touch_down(touch) 878 | print "clicked in menu" 879 | return 880 | print "not in menu" 881 | currentTool = self.mainTools.currentTool 882 | print "Tool is: " + currentTool 883 | ctouch['active'] = True 884 | 885 | 886 | if currentTool == 'spline': 887 | newspline = Spline.Spline(stepsize=1./self.mainTools.splineMenu.smoothnessSlider.value) 888 | newspline.add_or_select((pos[0]-150, pos[1]), 2) 889 | newspline.add_or_select((pos[0], pos[1]+170), 2) 890 | newspline.add_or_select((pos[0]+150, pos[1]), 2) 891 | newspline.DrawCurve() 892 | 893 | spline_ent_id = self.create_spline(pos,newspline,selectNow=True) 894 | #pg = PolyGen.PolyGen() 895 | #pg.from_spline(newspline.subpoints) 896 | #do_physics = self.mainTools.polyMenu.polyPhysButton.state != 'down' 897 | #spline_ent_id = self.create_poly(pos,pg,selectNow=True)#, do_physics=do_physics) 898 | spline_ent = self.getEntFromID(spline_ent_id) 899 | #self.mainTools.setEnt(spline_ent_id) 900 | shape = spline_ent.physics.shapes[0] 901 | #ctouch['polygen'] = pg 902 | self.mainTools.setTool('splineed') 903 | self.mainTools.splineMenu.splineButton.state = 'normal' 904 | self.mainTools.splineMenu.splineEdButton.state = 'down' 905 | 906 | 907 | if currentTool == 'splineed': 908 | ent = self.mainTools.selectedEntity 909 | if ent: 910 | if not hasattr(ent, 'splineshape') and hasattr(ent, 'polyshape'): 911 | pg = ent.polyshape 912 | cont = pg.poly[0] 913 | print len(cont) 914 | cp = [] 915 | for pindex in xrange(0, len(cont), 4): 916 | p=cont[pindex] 917 | cp.append(p) 918 | if len(cp)>4: 919 | newspline = Spline.Spline() 920 | newspline.ControlPoints = cp 921 | ent.splineshape = newspline 922 | 923 | if hasattr(ent, 'splineshape'): 924 | ss = ent.splineshape 925 | camera_scale = self.gameworld.systems['gameview'].camera_scale 926 | print camera_scale*300 927 | ss.add_or_select(pos, 40*camera_scale, 300*camera_scale) 928 | ss.DrawCurve() 929 | ent.polyshape.from_spline(ss.subpoints) 930 | 931 | 932 | spline_ent_id = self.create_poly(pos,ent.polyshape,ent.entity_id) 933 | spline_ent = self.getEntFromID(spline_ent_id) 934 | spline_ent.splineshape = ss 935 | shape = spline_ent.physics.shapes[0] 936 | #self.create_poly((0,0),pg,ent.entity_id) 937 | 938 | if currentTool == 'splinesub': 939 | ent = self.mainTools.selectedEntity 940 | if ent: 941 | if not hasattr(ent, 'splineshape') and hasattr(ent, 'polyshape'): 942 | pg = ent.polyshape 943 | cont = pg.poly[0] 944 | print len(cont) 945 | if hasattr(ent, 'splineshape'): 946 | print "entid=", ent.entity_id 947 | ss = ent.splineshape 948 | if len(ss.ControlPoints)>3: 949 | viewport = self.gameworld.systems['gameview'] 950 | ss.remove_point(pos, 40*viewport.camera_scale) 951 | ss.DrawCurve() 952 | ent.polyshape.from_spline(ss.subpoints) 953 | spline_ent_id = self.create_poly(pos,ent.polyshape,ent.entity_id) 954 | spline_ent = self.getEntFromID(spline_ent_id) 955 | spline_ent.splineshape = ss 956 | shape = spline_ent.physics.shapes[0] 957 | #self.create_poly((0,0),pg,ent.entity_id) 958 | 959 | if currentTool == 'polysub': 960 | polys = self.get_touching_polys(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 961 | for p in polys: 962 | p.polyshape.sub_circle_polygon(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 963 | self.create_poly(pos,p.polyshape,p.entity_id) 964 | 965 | 966 | 967 | if currentTool == 'poly': 968 | polys = [] 969 | lastpolyid = None 970 | if self.mainTools.polyMenu.polyMergeButton.state != 'normal': 971 | polys = self.get_touching_polys(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 972 | if len(polys)>0: 973 | e = polys[0] 974 | lastpolyid = e.entity_id 975 | pg = e.polyshape 976 | else: 977 | polyMenu = self.mainTools.polyMenu 978 | pg = PolyGen.PolyGen(keepsimple=polyMenu.polySimpleButton.state != 'normal', 979 | minlinelen=polyMenu.minlenslider.value) 980 | pg.draw_circle_polygon(pos, radius=self.mainTools.polyMenu.brushSizeSlider.value) 981 | do_physics = self.mainTools.polyMenu.polyPhysButton.state != 'down' 982 | ctouch['lastpolyid'] = self.create_poly(pos,pg, lastpolyid=lastpolyid, do_physics=do_physics) 983 | ctouch['polygen'] = pg 984 | 985 | if currentTool in ["draw", "square", "box", "circle", "plank"]: 986 | color = (1,1,1,1) 987 | if self.mainTools.selectedEntity and self.mainTools.cloneSpriteButton.state == 'down': 988 | c = self.mainTools.selectedEntity.color 989 | color = (c.r, c.g ,c.b ,c.a) 990 | ctouch['previewShape'] = self.create_decoration(pos=(0, 0), width=0, height=0, 991 | texture=self.mainTools.spriteSpinner.text, 992 | color=color) 993 | 994 | if shape and currentTool == 'del': 995 | if shape == self.mainTools.selectedItem: 996 | self.mainTools.setShape(None) 997 | self.delObj(shape.body.data) 998 | ctouch['touchingnow'] = None 999 | return 1000 | 1001 | 1002 | if currentTool == "paste" and self.mainTools.entcpy: 1003 | pastedEID = self.serials.loadEntFromDict(self.mainTools.entcpy) 1004 | ent = self.gameworld.entities[pastedEID] 1005 | if hasattr(ent, 'polyshape'): 1006 | po = ent.polyshape.poly 1007 | poc = po.center() 1008 | shifter = (pos[0] - poc[0], pos[1] - poc[1]) 1009 | if hasattr(ent, 'polyshape'): 1010 | ss = ent.splineshape 1011 | cps = ss.ControlPoints 1012 | for pindex in range(len(cps)): 1013 | p = cps[pindex] 1014 | cps[pindex]=(p[0]+shifter[0], p[1]+shifter[1]) 1015 | self.create_spline((0,0),ss,ent.entity_id) 1016 | else: 1017 | po.shift(shifter[0], shifter[1]) 1018 | self.create_poly((0,0),ent.polyshape,ent.entity_id) 1019 | elif hasattr(ent, 'physics'): 1020 | phys = ent.physics 1021 | phys.body.position = pos 1022 | shape = phys.shapes[0] 1023 | #self.mainTools.setShape(shape) 1024 | space.reindex_shape(shape) 1025 | ctouch['touching'] = shape 1026 | 1027 | canselect = currentTool in ['camera', 'drag', 'vortex','rotate', 'delete'] 1028 | if canselect: 1029 | if shape: 1030 | self.mainTools.setShape(shape) 1031 | else: 1032 | ents = self.getNonPhysAtPoint(pos) 1033 | ent = None 1034 | if len(ents):ent=ents[0] 1035 | self.mainTools.setEnt(ent) 1036 | 1037 | 1038 | if shape and not shape.body.is_static and ( 1039 | currentTool == 'drag' or currentTool == 'paste' or currentTool == 'pin'): 1040 | body = ctouch['ownbody'] 1041 | body.position = pos 1042 | ctouch['mousejoint'] = cy.PivotJoint(shape.body, body, position) 1043 | space.add(ctouch['mousejoint']) 1044 | def getNonPhysAtPoint(self, pos): 1045 | ents = [] 1046 | for aid in self.entIDs: 1047 | entity = self.gameworld.entities[aid] 1048 | if not hasattr(entity, 'physics'): 1049 | if hasattr(entity, 'polyshape'): 1050 | isin = entity.polyshape.poly.isInside(pos[0],pos[1]) 1051 | if isin:ents.append(entity) 1052 | else: 1053 | isin = self.get_point_in_renderer(pos,entity) 1054 | #isin = entity.polyshape.poly.isInside(pos[0],pos[1]) 1055 | if isin:ents.append(entity) 1056 | return ents 1057 | def get_point_in_renderer(self, point, ent): 1058 | if hasattr(ent, 'renderer'): 1059 | p = list(point) 1060 | p[0]-=ent.position.x 1061 | p[1]-=ent.position.y 1062 | x=p[0] 1063 | y=p[1] 1064 | angle = -ent.rotate.r 1065 | transp = [ 1066 | (x * cos(angle)) - (y * sin(angle)), 1067 | (y * cos(angle)) + (x * sin(angle))] 1068 | r = ent.renderer 1069 | #print transp, r.width/2., r.height/2. 1070 | if abs(transp[0]) < abs(r.width/2.) and abs(transp[1]) < abs(r.height/2.): 1071 | return True 1072 | return False 1073 | 1074 | def get_touching_polys(self, pos, radius=30): 1075 | cs = PolyGen.Circle(radius, pos, 16) 1076 | polys = [] 1077 | for eid in self.entIDs: 1078 | e = self.getEntFromID(eid) 1079 | if hasattr(e, "polyshape"): 1080 | if cs.overlaps(e.polyshape.poly): 1081 | polys.append(e) 1082 | return polys 1083 | def get_touching_polys_cp(self, pos, radius=30): 1084 | space = self.space 1085 | cs = cy.Circle(cy.Body(), radius=radius, offset=pos) 1086 | colshapes = space.shape_query(cs) 1087 | polys = [] 1088 | if len(colshapes)>0: 1089 | #print colshapes 1090 | ents = {} 1091 | for shape in colshapes: 1092 | id = shape.body.data 1093 | ents[id]=True 1094 | for eid in ents: 1095 | e = self.getEntFromID(eid) 1096 | if hasattr(e, "polyshape"): 1097 | polys.append(e) 1098 | return polys 1099 | 1100 | def clearAll(self): 1101 | self.startID = -1 1102 | self.finishID = -1 1103 | 1104 | self.mainTools.setShape(None) 1105 | space = self.space 1106 | print "clearing objects" 1107 | for eid in list(self.entIDs): 1108 | #print "beforedel" 1109 | self.delObj(eid) 1110 | #print "afterdel" 1111 | #space.remove(list(space.constraints)) 1112 | print "clearing joints" 1113 | for c in list(space.constraints): 1114 | self.deleteJoint(c) 1115 | def delObjNext(self, objid): 1116 | if objid not in self.todelete:self.todelete.append(objid) 1117 | def delObj(self, objid): 1118 | #todo check before removing these items 1119 | #print "removing:", objid 1120 | 1121 | ent = self.getEntFromID(objid) 1122 | if hasattr(ent, 'polyshape'): 1123 | delattr(ent, 'polyshape') 1124 | if hasattr(ent, 'splineshape'): 1125 | delattr(ent, 'splineshape') 1126 | if hasattr(ent, "physics"): 1127 | b = ent.physics.body 1128 | if b.data == self.startID:self.startID=None 1129 | if b.data == self.finishID:self.finishID=None 1130 | removeus = self.getJointsOnBody(b) 1131 | #for rmu in removeus: 1132 | # print rmu in self.space.constraints 1133 | for c in removeus: 1134 | #print "removing", c 1135 | self.deleteJoint(c) 1136 | #print ent, self.mainTools.selectedEntity 1137 | if ent == self.mainTools.selectedEntity: 1138 | self.mainTools.setShape(None) 1139 | self.gameworld.remove_entity(objid) 1140 | if objid in self.entIDs: self.entIDs.remove(objid) 1141 | def getJointsOnBody(self, b): 1142 | joints = [] 1143 | for c in self.space.constraints: 1144 | if c.a == b or c.b == b: 1145 | joints.append(c) 1146 | return joints 1147 | def getWorldPosFromTouch(self, touch): 1148 | return self.getWorldPosFromTuple((touch.x,touch.y)) 1149 | 1150 | def getWorldPosFromTuple(self, tup): 1151 | 1152 | viewport = self.gameworld.systems['gameview'] 1153 | return tup[0]*viewport.camera_scale - viewport.camera_pos[0], tup[1]*viewport.camera_scale - viewport.camera_pos[1] 1154 | 1155 | def update(self, dt): 1156 | for o in self.todelete: 1157 | self.delObj(o) 1158 | self.todelete = [] 1159 | self.mainTools.update(dt) 1160 | ent = self.mainTools.selectedEntity 1161 | 1162 | if self.selectedShapeID != None and ent != None: 1163 | sbox = self.getEntFromID(self.selectedShapeID) 1164 | sbox.position.x =ent.position.x 1165 | sbox.position.y =ent.position.y 1166 | bb = ent.physics.shapes[0].cache_bb() 1167 | sbox.renderer.width = (bb['r']-bb['l'])*1.05+5 1168 | sbox.renderer.height = (bb['t']-bb['b'])*1.05+5 1169 | for j, je in self.jointEnts.iteritems(): 1170 | #j = je.joint 1171 | b1l = j.a.local_to_world(cy.Vec2d(j.anchor1['x'],j.anchor1['y'])) 1172 | b2l = j.b.local_to_world(cy.Vec2d(j.anchor2['x'],j.anchor2['y'])) 1173 | b1l = cy.Vec2d(b1l['x'], b1l['y']) 1174 | b2l = cy.Vec2d(b2l['x'], b2l['y']) 1175 | xd = b1l.x - b2l.x 1176 | yd = b1l.y - b2l.y 1177 | midx = (b1l.x + b2l.x) / 2.0 1178 | midy = (b1l.y + b2l.y) / 2.0 1179 | dist = sqrt(xd ** 2 + yd ** 2) 1180 | angle = atan2(yd, xd) 1181 | self.setEntIDPosSizeRot(je.entity_id, midx,midy,dist,10, angle) 1182 | #self.setEntIDPosSizeRot(je.entity_id, midx,midy,xd,yd) 1183 | if self.mainTools.killMomem: 1184 | for aid in self.entIDs: 1185 | entity = self.gameworld.entities[aid] 1186 | if hasattr(entity, 'physics') and entity.physics.body.is_static == 0: 1187 | v = entity.physics.body.velocity 1188 | entity.physics.body.velocity = (v[0]*0.1,v[1]*0.1) 1189 | entity.physics.body.angular_velocity *=0.1 1190 | 1191 | if not self.mainTools.paused: 1192 | self.gameworld.update(dt) 1193 | for t in self.touches: 1194 | ctouch = self.touches[t] 1195 | if ctouch['active']: 1196 | pos = ctouch['newpos'] 1197 | if ctouch['tool'] == 'vortex': 1198 | self.pull2point(pos) 1199 | elif ctouch['tool'] == 'del' and 'touchingnow' in ctouch: 1200 | shape = self.getShapeAt(pos[0], pos[1]) 1201 | if shape: 1202 | self.delObj(shape.body.data) 1203 | ctouch['touchingnow'] = None 1204 | 1205 | def pull2point(self, pos): 1206 | for aid in self.entIDs: 1207 | entity = self.gameworld.entities[aid] 1208 | if hasattr(entity, 'physics') and entity.physics.body.is_static == 0: 1209 | apos = entity.position 1210 | dvecx = (pos[0] - apos.x) * entity.physics.body.mass * 0.02 1211 | dvecy = (pos[1] - apos.y) * entity.physics.body.mass * 0.02 1212 | entity.physics.body.apply_impulse((dvecx, dvecy)) 1213 | #entity.physics.body.apply_force((dvecx,dvecy)) 1214 | 1215 | def setup_states(self): 1216 | self.gameworld.add_state(state_name='main', 1217 | systems_added=['color', 'rotate', 'renderer', 'physics_renderer'], 1218 | systems_removed=[], systems_paused=[], 1219 | systems_unpaused=['color', 'rotate', 'renderer', 'physics_renderer'], 1220 | screenmanager_screen='main') 1221 | 1222 | def set_state(self): 1223 | self.gameworld.state = 'main' 1224 | 1225 | 1226 | 1227 | class KivEntEd(App): 1228 | def build(self): 1229 | Window.clearcolor = (0, 0, 0, 1.) 1230 | dataDir = self.get_application_storage_dir() 1231 | print "dd="+dataDir 1232 | if not os.path.exists(dataDir): 1233 | os.makedirs(dataDir) 1234 | self.root.dataDir = dataDir 1235 | 1236 | def on_pause(self): 1237 | print "pausing" 1238 | self.root.serials.exportJSON(fileName="pauselevel.json") 1239 | return True 1240 | def on_resume(self): 1241 | pass 1242 | #self.root.clearAll() 1243 | #self.root.serials.loadJSON(fileName="pauselevel.json") 1244 | 1245 | 1246 | def get_application_storage_dir(self, extra=""): 1247 | defaultpath = '~/.%(appname)s/' 1248 | if platform == 'android': 1249 | defaultpath = '/sdcard/.%(appname)s/' 1250 | elif platform == 'ios': 1251 | defaultpath = '~/Documents/%(appname)s/' 1252 | elif platform == 'win': 1253 | defaultpath = defaultpath.replace('/', sep) 1254 | defaultpath+=extra 1255 | return expanduser(defaultpath) % { 1256 | 'appname': self.name, 'appdir': self.directory} 1257 | #return super(KivEntEd, self).get_application_config( 1258 | # '~/.%(appname)s/' + extra) 1259 | 1260 | def get_application_config(self): 1261 | return self.get_application_storage_dir("%(appname)s.ini") 1262 | '''def on_start(self): 1263 | self.profile = cProfile.Profile() 1264 | self.profile.enable() 1265 | 1266 | def on_stop(self): 1267 | self.profile.disable() 1268 | self.profile.dump_stats('myapp.profile')''' 1269 | 1270 | 1271 | def uploadCrash(crashstr): 1272 | print "uploading crash to ", ui_elements.serverURL 1273 | #req = UrlRequest('/listLevels', on_success=self.got_levels, timeout=1000) 1274 | import urllib 1275 | from kivy.network.urlrequest import UrlRequest 1276 | params = urllib.urlencode({ 1277 | 'version':__version__, 1278 | "crashData":crashstr 1279 | }) 1280 | headers = {'Content-type': 'application/x-www-form-urlencoded', 1281 | 'Accept': 'text/plain'} 1282 | req = UrlRequest(ui_elements.serverURL+'/uploadCrash', on_success=None, req_body=params, 1283 | req_headers=headers) 1284 | req.wait() 1285 | print "crash uploaded" 1286 | 1287 | if __name__ == '__main__': 1288 | try: 1289 | KivEntEd().run() 1290 | except: 1291 | print "unexpected error" 1292 | import traceback 1293 | traceback.print_exc() 1294 | print "---" 1295 | uploadCrash(traceback.format_exc()) 1296 | print "---" 1297 | raise 1298 | --------------------------------------------------------------------------------