├── BDX └── bdxhelper │ └── src │ └── com │ └── solarlune │ └── bdxhelper │ ├── choice │ ├── Choice.java │ └── Screen.java │ ├── components │ ├── ColorChange.java │ ├── ai │ │ ├── Behavior.java │ │ ├── Director.java │ │ ├── Track.java │ │ └── behaviors │ │ │ ├── MoveTowardTarget.java │ │ │ ├── Shoot.java │ │ │ └── Wait.java │ ├── inventory │ │ ├── Inventory.java │ │ └── Item.java │ ├── mesh │ │ ├── AsepriteAnim.java │ │ └── Trail.java │ ├── movement │ │ ├── ConstantMotion.java │ │ ├── GroundRay.java │ │ ├── LockMotion.java │ │ ├── LockToGridComponent.java │ │ └── SnapAxis.java │ ├── particles │ │ ├── Emitter.java │ │ └── Particle.java │ └── properties │ │ ├── Gauge.java │ │ ├── RoomIdentifier2D.java │ │ ├── TeamComponent.java │ │ ├── TimedEnd.java │ │ └── TouchDamage.java │ ├── input │ └── GamepadProfiles.java │ ├── navigation │ └── NodeMap.java │ ├── randgen │ ├── Cell.java │ └── Map.java │ └── utils │ ├── API.java │ ├── BDXHMath.java │ └── Notifications.java ├── BGE ├── API Stub │ ├── aud.py │ ├── bge.py │ ├── bgl.py │ ├── blf.py │ └── mathutils.py └── bghelper │ ├── __init__.py │ ├── api.py │ ├── audio.py │ ├── example.blend │ ├── filters.py │ ├── game.py │ ├── input.py │ ├── light.py │ ├── math.py │ ├── mesh.py │ ├── nodemap.py │ ├── rlg.py │ ├── sprites.py │ ├── time.py │ ├── trail.py │ ├── viewport.py │ └── window.py ├── LICENSE └── README.md /BDX/bdxhelper/src/com/solarlune/bdxhelper/choice/Choice.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.choice; 2 | 3 | import com.nilunder.bdx.utils.Named; 4 | 5 | import javax.vecmath.Vector3f; 6 | 7 | /** 8 | * Created by solarlune on 8/7/16. 9 | */ 10 | public class Choice implements Named { 11 | 12 | private String name; 13 | public Screen screen; 14 | public Vector3f position = new Vector3f(); 15 | 16 | private Choice up; 17 | private Choice down; 18 | private Choice left; 19 | private Choice right; 20 | 21 | public Choice(String name){ 22 | name(name); 23 | } 24 | 25 | public void name(String name){ 26 | this.name = name; 27 | } 28 | 29 | public String name(){ 30 | return name; 31 | } 32 | 33 | public void right(Choice choice){ 34 | right = choice; 35 | if (choice.left == null) 36 | choice.left = this; 37 | } 38 | 39 | public void right(String choiceName){ 40 | right(screen.choices.get(choiceName)); 41 | } 42 | 43 | public Choice right(){ 44 | return right; 45 | } 46 | 47 | public void clearRight(){ 48 | right = null; 49 | } 50 | 51 | public void left(Choice choice) { 52 | left = choice; 53 | if (choice.right == null) 54 | choice.right = this; 55 | } 56 | 57 | public void left(String choiceName) { 58 | left(screen.choices.get(choiceName)); 59 | } 60 | 61 | public Choice left(){ 62 | return left; 63 | } 64 | 65 | public void clearLeft(){ 66 | left = null; 67 | } 68 | 69 | public void up(Choice choice) { 70 | up = choice; 71 | if (choice.down == null) 72 | choice.down = this; 73 | } 74 | 75 | public void up(String choiceName) { 76 | up(screen.choices.get(choiceName)); 77 | } 78 | 79 | public Choice up(){ 80 | return up; 81 | } 82 | 83 | public void clearUp(){ 84 | up = null; 85 | } 86 | 87 | public void down(Choice choice) { 88 | down = choice; 89 | if (choice.up == null) 90 | choice.up = this; 91 | } 92 | 93 | public void down(String choiceName) { 94 | down(screen.choices.get(choiceName)); 95 | } 96 | 97 | public Choice down(){ 98 | return down; 99 | } 100 | 101 | public void clearDown(){ 102 | down = null; 103 | } 104 | 105 | public void clearNeighbors(){ 106 | clearUp(); 107 | clearRight(); 108 | clearDown(); 109 | clearLeft(); 110 | } 111 | 112 | public String toString(){ 113 | return "Choice: " + name(); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/choice/Screen.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.choice; 2 | 3 | import com.nilunder.bdx.Scene; 4 | import com.nilunder.bdx.utils.ArrayListNamed; 5 | import com.nilunder.bdx.utils.Named; 6 | 7 | import javax.vecmath.Vector3f; 8 | 9 | import java.util.ArrayList; 10 | 11 | /** 12 | * Created by SolarLune on 1/28/2015. 13 | */ 14 | 15 | public class Screen { 16 | 17 | public enum BoundsMode { 18 | STOP, 19 | LOOP, 20 | } 21 | 22 | public enum AutoNeighborChoice { 23 | UP, 24 | RIGHT, 25 | DOWN, 26 | LEFT 27 | } 28 | 29 | public ArrayListNamed choices = new ArrayListNamed(); 30 | public int loopSize = -1; 31 | public BoundsMode boundsMode = BoundsMode.STOP; 32 | 33 | public String name; 34 | public Choice activeChoice; 35 | 36 | Vector3f areaStart = new Vector3f(); 37 | Vector3f areaEnd = new Vector3f(); 38 | 39 | public void add(Choice c){ 40 | if (activeChoice == null) 41 | activeChoice = c; 42 | choices.add(c); 43 | c.screen = this; 44 | } 45 | 46 | public ArrayListNamed add(String... choiceNames) { 47 | 48 | ArrayListNamed addedChoices = new ArrayListNamed(); 49 | 50 | for (String s : choiceNames) { 51 | Choice c = new Choice(s); 52 | addedChoices.add(c); 53 | add(c); 54 | } 55 | 56 | return addedChoices; 57 | } 58 | 59 | public void autoChoiceScroll(AutoNeighborChoice scrollMode) { 60 | 61 | for (int i = 0; i < choices.size(); i++) { 62 | 63 | Choice c = choices.get(i); 64 | 65 | Choice n = null; 66 | if (i < choices.size() - 1) 67 | n = choices.get(i + 1); 68 | 69 | Choice p = null; 70 | if (i > 0) 71 | p = choices.get(i - 1); 72 | 73 | if (n == null) // At the end 74 | break; 75 | 76 | if (scrollMode == AutoNeighborChoice.UP) { 77 | c.up(n); 78 | n.down(c); 79 | } 80 | else if (scrollMode == AutoNeighborChoice.RIGHT) { 81 | c.right(n); 82 | n.left(c); 83 | } 84 | else if (scrollMode == AutoNeighborChoice.DOWN) { 85 | c.down(n); 86 | n.up(c); 87 | } 88 | else { 89 | c.left(n); 90 | n.right(c); 91 | } 92 | 93 | if (p != null) { 94 | if (scrollMode == AutoNeighborChoice.UP) { 95 | c.down(p); 96 | p.up(c); 97 | } 98 | else if (scrollMode == AutoNeighborChoice.RIGHT) { 99 | c.left(p); 100 | p.right(c); 101 | } 102 | else if (scrollMode == AutoNeighborChoice.DOWN) { 103 | c.up(p); 104 | p.down(c); 105 | } 106 | else { 107 | c.right(p); 108 | p.left(c); 109 | } 110 | } 111 | 112 | } 113 | 114 | } 115 | 116 | public void autoChoiceScroll(){ 117 | autoChoiceScroll(AutoNeighborChoice.DOWN); 118 | } 119 | 120 | public void resetChoiceNeighbors(){ 121 | for (Choice c : choices) { 122 | c.clearNeighbors(); 123 | } 124 | } 125 | 126 | public void remove(Choice c){ 127 | choices.remove(c); 128 | } 129 | 130 | public void clearChoices(){ 131 | choices.clear(); 132 | } 133 | 134 | public void up(){ 135 | if (activeChoice.up() != null) 136 | activeChoice = activeChoice.up(); 137 | else if (boundsMode == BoundsMode.LOOP) { 138 | Choice n = activeChoice; 139 | while (n.down() != null) { 140 | n = n.down(); 141 | } 142 | activeChoice = n; 143 | } 144 | } 145 | 146 | public void right(){ 147 | if (activeChoice.right() != null) 148 | activeChoice = activeChoice.right(); 149 | else if (boundsMode == BoundsMode.LOOP) { 150 | Choice n = activeChoice; 151 | while (n.left() != null) { 152 | n = n.left(); 153 | } 154 | activeChoice = n; 155 | } 156 | } 157 | 158 | public void down(){ 159 | if (activeChoice.down() != null) 160 | activeChoice = activeChoice.down(); 161 | else if (boundsMode == BoundsMode.LOOP) { 162 | Choice n = activeChoice; 163 | while (n.up() != null) { 164 | n = n.up(); 165 | } 166 | activeChoice = n; 167 | } 168 | } 169 | 170 | public void left(){ 171 | if (activeChoice.left() != null) 172 | activeChoice = activeChoice.left(); 173 | else if (boundsMode == BoundsMode.LOOP) { 174 | Choice n = activeChoice; 175 | while (n.right() != null) { 176 | n = n.right(); 177 | } 178 | activeChoice = n; 179 | } 180 | } 181 | 182 | public void autoChoicePosition(Scene scene){ 183 | for (Choice c : choices) { 184 | if (scene.objects.get(c.name()) != null) 185 | c.position.set(scene.objects.get(c.name()).position()); 186 | } 187 | } 188 | 189 | // public Choice setChoice(int index){ 190 | // if (active) 191 | // currentChoiceIndex = index; 192 | // enforceBounds(); 193 | // return choices.get(currentChoiceIndex); 194 | // } 195 | // 196 | // public Choice skipChoice(int skip){ 197 | // return setChoice(currentChoiceIndex + skip); 198 | // } 199 | 200 | // public void enforceBounds(){ 201 | // 202 | // if (boundsMode == BoundsMode.LOOP) { 203 | // 204 | // int skip = loopSize == -1 ? choices.size() : loopSize + 1; 205 | // 206 | // if (currentChoiceIndex >= choices.size()) { 207 | // currentChoiceIndex -= skip; 208 | // } 209 | // if (currentChoiceIndex < 0) { 210 | // currentChoiceIndex += skip; 211 | // } 212 | // 213 | // } 214 | // else if (boundsMode == BoundsMode.STOP) { 215 | // currentChoiceIndex = BDXHMath.max(BDXHMath.min(currentChoiceIndex, choices.size() - 1), 0); 216 | // } 217 | // 218 | // } 219 | 220 | // public Choice currentChoice(){ 221 | // enforceBounds(); 222 | // return choices.get(currentChoiceIndex); 223 | // } 224 | 225 | public void zone(Vector3f start, Vector3f end){ 226 | areaStart.set(start); 227 | areaEnd.set(end); 228 | } 229 | 230 | public boolean on(String choiceName) { 231 | return activeChoice.name().equals(choiceName); 232 | } 233 | 234 | public int getChoiceIndex(Choice c){ 235 | return choices.indexOf(c); 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ColorChange.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | import com.nilunder.bdx.utils.Color; 7 | import com.nilunder.bdx.utils.Timer; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | 12 | /** 13 | * Created by SolarLune on 2/2/2015. 14 | */ 15 | public class ColorChange extends Component { 16 | 17 | public enum FinishStrategy { 18 | PING_PONG, // A > B > A > B > A ... 19 | END, // A > B, Turn off 20 | LOOP, // A > B, A > B, A > B... 21 | } 22 | 23 | public ArrayList colors = new ArrayList(); 24 | public Timer changeTimer = new Timer(); 25 | public FinishStrategy onFinish = FinishStrategy.PING_PONG; 26 | public boolean on = false; 27 | int colorIndex = 0; 28 | int playDir = 1; 29 | private boolean completedCycle = false; 30 | 31 | public ColorChange(GameObject g){ 32 | super(g); 33 | state(mainState); 34 | } 35 | 36 | State mainState = new State(){ 37 | 38 | public void main(){ 39 | 40 | completedCycle = false; 41 | 42 | if (on && colors.size() > 1) { 43 | 44 | g.visibleNoChildren(true); 45 | 46 | if (changeTimer.done()) { 47 | 48 | if (colorIndex + playDir > 1 && colorIndex + playDir < colors.size() - 1) { 49 | colorIndex += playDir; 50 | changeTimer.restart(); 51 | } else { 52 | 53 | if (onFinish == FinishStrategy.LOOP) { 54 | changeTimer.restart(); 55 | colorIndex = 0; 56 | completedCycle = true; 57 | } 58 | else if (onFinish == FinishStrategy.PING_PONG) { 59 | colorIndex += playDir; 60 | playDir *= -1; 61 | changeTimer.restart(); 62 | if (colorIndex == 0) 63 | completedCycle = true; 64 | 65 | } else if (onFinish == FinishStrategy.END) { 66 | on = false; 67 | colorIndex = 0; 68 | completedCycle = true; 69 | } 70 | 71 | } 72 | 73 | } 74 | 75 | Color startingColor = colors.get(colorIndex); 76 | Color targetColor = colors.get(colorIndex + playDir); 77 | 78 | Color c = new Color(startingColor); 79 | c.lerp(targetColor, 1 - (changeTimer.timeLeft() / changeTimer.interval)); 80 | g.mesh().materials.get(0).color(c); 81 | 82 | } 83 | 84 | } 85 | 86 | }; 87 | 88 | public void glow(float time, Color... colors){ 89 | 90 | this.colors.clear(); 91 | 92 | Collections.addAll(this.colors, colors); 93 | 94 | on = true; 95 | 96 | colorIndex = 0; 97 | 98 | changeTimer.set(time); 99 | 100 | } 101 | 102 | public void glow() { 103 | glow(1, g.mesh().materials.get(0).color(), new Color(1, 1, 1, 1)); // Just a quick default to try 104 | } 105 | 106 | public void flash(Color targetColor){ 107 | onFinish = FinishStrategy.END; 108 | glow(1, new Color(), targetColor); 109 | changeTimer.done(true); 110 | } 111 | 112 | public void flash(){ 113 | if (g.mesh().materials.get(0).color().a <= 0.5f) 114 | flash(new Color(1, 1, 1, 1)); 115 | else 116 | flash(new Color(1, 1, 1, 0)); 117 | } 118 | 119 | public boolean completedCycle(){ 120 | return completedCycle; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/Behavior.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai; 2 | 3 | import com.nilunder.bdx.GameObject; 4 | import com.nilunder.bdx.State; 5 | 6 | /** 7 | * Created by SolarLuneNew on 4/1/2016. 8 | */ 9 | 10 | public class Behavior extends State { 11 | 12 | public GameObject g; 13 | 14 | public Behavior(GameObject g){ 15 | this.g = g; 16 | } 17 | 18 | public boolean done(){ 19 | return true; 20 | } 21 | 22 | public String name(){ 23 | return this.getClass().getSimpleName(); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/Director.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | import com.nilunder.bdx.utils.ArrayListNamed; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class Director extends Component { 11 | 12 | public ArrayListNamed tracks = new ArrayListNamed(); 13 | 14 | public Director(GameObject g){ 15 | super(g); 16 | state = main; 17 | tracks.add(new Track()); 18 | } 19 | 20 | State main = new State(){ 21 | 22 | public void main(){ 23 | for (Track track : tracks) { 24 | if (track.active) 25 | track.main(); 26 | } 27 | } 28 | 29 | }; 30 | 31 | public Track active(){ 32 | Track t = null; 33 | if (activeTracks().size() > 0) 34 | t = activeTracks().get(0); 35 | return t; 36 | } 37 | 38 | public ArrayList activeTracks(){ 39 | ArrayList actives = new ArrayList(); 40 | for (Track t : tracks) { 41 | if (t.active) 42 | actives.add(t); 43 | } 44 | return actives; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/Track.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai; 2 | 3 | import com.nilunder.bdx.utils.Named; 4 | import com.nilunder.bdx.utils.Random; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * Created by SolarLuneNew on 4/1/2016. 10 | */ 11 | public class Track extends ArrayList implements Named { 12 | 13 | public enum TrackPlay { 14 | NORMAL, 15 | CHOOSE 16 | } 17 | 18 | public enum TrackEnd { 19 | LOOP, 20 | PINGPONG, 21 | STOP 22 | } 23 | 24 | public int behaviorIndex; 25 | private int prevBehaviorIndex; 26 | private boolean justChanged = true; 27 | public TrackPlay playMode = TrackPlay.NORMAL; 28 | public TrackEnd onEnd = TrackEnd.LOOP; 29 | public int behaviorPlayRate = 1; 30 | public boolean playing = true; 31 | public boolean active = true; 32 | public String name; 33 | 34 | public Behavior currentBehavior; 35 | 36 | public boolean add(Behavior... behaviors){ 37 | for (Behavior b : behaviors) 38 | add(b); 39 | return true; 40 | } 41 | 42 | public Track(String name){ 43 | this.name = name; 44 | } 45 | 46 | public Track(){ 47 | this("default"); 48 | } 49 | 50 | public void main(){ 51 | 52 | if (size() > 0 && playing) { 53 | 54 | if (currentBehavior != this.get(behaviorIndex)) { 55 | currentBehavior = this.get(behaviorIndex); 56 | currentBehavior.enter(); 57 | justChanged = true; 58 | } 59 | 60 | justChanged = false; 61 | 62 | currentBehavior.main(); 63 | 64 | if (currentBehavior.done()) { 65 | 66 | prevBehaviorIndex = behaviorIndex; 67 | 68 | if (playMode == TrackPlay.NORMAL) 69 | behaviorIndex += behaviorPlayRate; 70 | else if (playMode == TrackPlay.CHOOSE) 71 | behaviorIndex = (int) Random.random(0, size()); 72 | 73 | currentBehavior.exit(); 74 | 75 | if (onEnd == TrackEnd.LOOP) { 76 | loopIndex(); 77 | } 78 | else if (onEnd == TrackEnd.PINGPONG) { 79 | capIndex(); 80 | if (behaviorIndex >= size() || behaviorIndex < 0) 81 | behaviorPlayRate = -behaviorPlayRate; 82 | } 83 | else if (onEnd == TrackEnd.STOP) { 84 | capIndex(); 85 | playing = false; 86 | } 87 | 88 | currentBehavior = this.get(behaviorIndex); 89 | currentBehavior.enter(); 90 | justChanged = true; 91 | 92 | } 93 | 94 | } 95 | 96 | } 97 | 98 | private void loopIndex(){ 99 | while (behaviorIndex >= size()) 100 | behaviorIndex -= size(); 101 | while (behaviorIndex < 0) 102 | behaviorIndex += size(); 103 | } 104 | 105 | private void capIndex(){ 106 | behaviorIndex = Math.max(0, Math.min(size() - 1, behaviorIndex)); 107 | } 108 | 109 | public String name(){ 110 | return name; 111 | } 112 | 113 | public Behavior next() { 114 | if (behaviorPlayRate > 0) { 115 | if (behaviorIndex + behaviorPlayRate < size()) 116 | return get(behaviorIndex + behaviorPlayRate); 117 | else 118 | return get(behaviorIndex + behaviorPlayRate - size()); 119 | } else { 120 | if (behaviorIndex + behaviorPlayRate > 0) 121 | return get(behaviorIndex + behaviorPlayRate); 122 | else 123 | return get(behaviorIndex + behaviorPlayRate + size()); 124 | } 125 | } 126 | 127 | public Behavior prev() { 128 | if (behaviorPlayRate > 0) { 129 | if (behaviorIndex - behaviorPlayRate > 0) 130 | return get(behaviorIndex - behaviorPlayRate); 131 | else 132 | return get(behaviorIndex - behaviorPlayRate + size()); 133 | } else { 134 | if (behaviorIndex - behaviorPlayRate < size()) 135 | return get(behaviorIndex - behaviorPlayRate); 136 | else 137 | return get(behaviorIndex - behaviorPlayRate - size()); 138 | } 139 | } 140 | 141 | public boolean justChanged(){ 142 | return justChanged; 143 | } 144 | 145 | public boolean atEnd(){ 146 | return behaviorIndex >= size() - 1; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/behaviors/MoveTowardTarget.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai.behaviors; 2 | 3 | import com.nilunder.bdx.GameObject; 4 | import com.solarlune.bdxhelper.components.ai.Behavior; 5 | 6 | import javax.vecmath.Vector3f; 7 | 8 | public class MoveTowardTarget extends Behavior { 9 | 10 | public GameObject targetObject; 11 | public Vector3f targetPosition; 12 | public float acceleration; 13 | public float maxSpeed; 14 | public float minDistance; 15 | public float maxDistance; 16 | public String moveAxes; 17 | 18 | public MoveTowardTarget(GameObject g, GameObject target){ 19 | this(g, target, 0.1f, 4.0f, 0, 0); 20 | } 21 | 22 | public MoveTowardTarget(GameObject g, GameObject target, float acceleration, float maxSpeed, float minDistance, float maxDistance) { 23 | super(g); 24 | this.targetObject = target; 25 | this.acceleration = acceleration; 26 | this.maxSpeed = maxSpeed; 27 | this.minDistance = minDistance; 28 | this.maxDistance = maxDistance; 29 | targetPosition = new Vector3f(0, 0, 0); 30 | moveAxes = "xyz"; 31 | } 32 | 33 | public void main(){ 34 | 35 | Vector3f targetPosition = null; 36 | String axes = moveAxes.toLowerCase(); 37 | 38 | if (targetObject != null) 39 | targetPosition = targetObject.position(); 40 | else 41 | targetPosition = this.targetPosition; 42 | 43 | if (targetPosition != null) { 44 | 45 | Vector3f vect = new Vector3f(); 46 | 47 | if (targetPosition.minus(g.position()).length() > maxDistance) 48 | vect.set(targetPosition.minus(g.position())); 49 | 50 | if (targetPosition.minus(g.position()).length() < minDistance) 51 | vect.set(g.position().minus(targetPosition)); 52 | 53 | if (!axes.contains("x")) 54 | vect.x = 0; 55 | if (!axes.contains("y")) 56 | vect.y = 0; 57 | if (!axes.contains("z")) 58 | vect.z = 0; 59 | 60 | if (vect.length() > 0) 61 | vect.length(maxSpeed); 62 | 63 | vect = g.velocity().plus(vect.minus(g.velocity()).mul(acceleration)); 64 | 65 | if (!axes.contains("x")) 66 | vect.x = 0; 67 | if (!axes.contains("y")) 68 | vect.y = 0; 69 | if (!axes.contains("z")) 70 | vect.z = 0; 71 | 72 | if (vect.length() > 0) 73 | vect.length(Math.min(vect.length(), maxSpeed)); 74 | 75 | if (!axes.contains("z")) 76 | vect.z = g.velocity().z; 77 | 78 | g.velocity(vect); 79 | 80 | } 81 | 82 | } 83 | 84 | public boolean tooFar(){ 85 | Vector3f targetPos; 86 | if (targetObject != null) 87 | targetPos = targetObject.position(); 88 | else 89 | targetPos = targetPosition; 90 | return targetPos.minus(g.position()).length() > maxDistance; 91 | } 92 | 93 | public boolean tooClose(){ 94 | Vector3f targetPos; 95 | if (targetObject != null) 96 | targetPos = targetObject.position(); 97 | else 98 | targetPos = targetPosition; 99 | return targetPos.minus(g.position()).length() < minDistance; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/behaviors/Shoot.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai.behaviors; 2 | 3 | import com.badlogic.gdx.utils.JsonValue; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.utils.Color; 6 | import com.nilunder.bdx.utils.Random; 7 | import com.solarlune.bdxhelper.components.ai.Behavior; 8 | 9 | import javax.vecmath.Matrix3f; 10 | import javax.vecmath.Vector3f; 11 | 12 | /** 13 | * Created by SolarLuneNew on 4/1/2016. 14 | */ 15 | public class Shoot extends Behavior { 16 | 17 | public String bulletObject; 18 | public Vector3f bulletDirection; 19 | public Vector3f spawnOffset; 20 | public float bulletSpeed; 21 | public Color bulletColor; 22 | public String bulletProp; 23 | public float bulletDeviance; 24 | public boolean alignBulletToDirection = false; 25 | 26 | private GameObject spawnedBullet; 27 | 28 | public Shoot(GameObject g, String bullet) { 29 | this(g, bullet, new Vector3f(0, 1, 0), new Vector3f(), 10, new Color(1, 1, 1, 1), null, 0.05f); 30 | } 31 | 32 | public Shoot(GameObject g, String bullet, Vector3f bulletDirection, Vector3f spawnOffset, float bulletSpeed, Color bulletColor, String prop, float shotDeviance){ 33 | super(g); 34 | this.bulletObject = bullet; 35 | this.bulletDirection = bulletDirection; 36 | this.bulletSpeed = bulletSpeed; 37 | this.spawnOffset = spawnOffset; 38 | if (bulletColor == null) 39 | this.bulletColor = new Color(1, 1, 1, 1); 40 | else 41 | this.bulletColor = bulletColor; 42 | this.bulletProp = prop; 43 | this.bulletDeviance = shotDeviance; 44 | } 45 | 46 | public void main(){ 47 | GameObject b = g.scene.add(bulletObject); 48 | b.position(g.position().plus(spawnOffset)); 49 | b.mesh().materials.color(bulletColor); 50 | for (GameObject c : b.childrenRecursive()) 51 | c.mesh().materials.color(bulletColor); 52 | if (bulletProp != null) 53 | b.props.put(bulletProp, new JsonValue(true)); 54 | if (alignBulletToDirection) 55 | b.alignAxisToVec(0, bulletDirection); 56 | Vector3f vel = bulletDirection.mul(bulletSpeed).rotated(new Vector3f(0, 1, 0), Random.random(-bulletDeviance, bulletDeviance)); 57 | b.velocity(vel); 58 | spawnedBullet = b; 59 | } 60 | 61 | public GameObject getLastBullet(){ 62 | return spawnedBullet; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/ai/behaviors/Wait.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.ai.behaviors; 2 | 3 | import com.nilunder.bdx.GameObject; 4 | import com.nilunder.bdx.utils.Timer; 5 | import com.solarlune.bdxhelper.components.ai.Behavior; 6 | 7 | /** 8 | * Created by SolarLuneNew on 4/1/2016. 9 | */ 10 | public class Wait extends Behavior { 11 | 12 | public Timer waitTimer; 13 | 14 | public Wait(GameObject g) { 15 | this(g, 1); 16 | } 17 | 18 | public Wait(GameObject g, float waitTime) { 19 | super(g); 20 | waitTimer = new Timer(waitTime); 21 | } 22 | 23 | public void enter(){ 24 | waitTimer.restart(); 25 | } 26 | 27 | public boolean done(){ 28 | return waitTimer.tick(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/inventory/Inventory.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.inventory; 2 | 3 | import java.util.ArrayList; 4 | 5 | import com.nilunder.bdx.Component; 6 | import com.nilunder.bdx.GameObject; 7 | 8 | public class Inventory extends Component { 9 | 10 | GameObject g; 11 | public int maxNumberOfSlots = 999; 12 | public ArrayList data = new ArrayList(); 13 | 14 | public Inventory(GameObject g){ 15 | super(g); 16 | logicFrequency = 0; 17 | } 18 | 19 | public boolean add(int index, Item item) { 20 | 21 | if (item.canBeCollected) { 22 | 23 | boolean stack = false; 24 | 25 | for (Item x : data) { 26 | 27 | if (x.id.equals(item.id) && x.quantity + item.quantity < x.stackLimit) { 28 | x.quantity += item.quantity; 29 | stack = true; 30 | } 31 | } 32 | 33 | if (!stack) { 34 | if (data.size() < maxNumberOfSlots) 35 | data.add(index, item); 36 | } 37 | 38 | item.collect(); 39 | 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | 46 | public boolean add(Item item) { 47 | return add(data.size(), item); 48 | } 49 | 50 | public Item get(int index) { 51 | return data.get(index); 52 | } 53 | 54 | public Item drop(int index) { 55 | if (data.size() > index && data.get(index).canBeDropped) { 56 | Item i = data.remove(index); 57 | i.drop(g.scene); 58 | return i; 59 | } 60 | return null; 61 | } 62 | 63 | public void drop(Item item) { 64 | if (data.contains(item) && item.canBeDropped) { 65 | data.remove(item); 66 | item.drop(g.scene); 67 | } 68 | } 69 | 70 | public int size(){ 71 | return data.size(); 72 | } 73 | 74 | public void empty(){ 75 | for (Item i : data) { 76 | i.drop(g.scene); 77 | } 78 | data.clear(); 79 | } 80 | 81 | public String toString(){ 82 | return data.toString(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/inventory/Item.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.inventory; 2 | 3 | import com.nilunder.bdx.Bdx; 4 | import com.nilunder.bdx.Component; 5 | import com.nilunder.bdx.GameObject; 6 | import com.nilunder.bdx.Scene; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | 11 | /** 12 | * Created by solarlune on 8/2/16. 13 | */ 14 | public class Item extends Component { 15 | 16 | public String name; // Name of the item; defaults to name of the owner GameObject 17 | public String id; // ID; used for stacking (determining if two items are basically the same); defaults to name 18 | public String description; // Description, if you so choose to have one 19 | public HashMap stats = new HashMap(); // Used to store basic statistics about the item (attack, defense, durability, rarity, elements, cost, etc) 20 | 21 | public float creationTimestamp = Bdx.time; // When the Item was instantiated 22 | public float collectionTimestamp; // When the Item was last collected 23 | public float dropTimestamp; // When the Item was last dropped 24 | 25 | public GameObject owner; 26 | public boolean canBeCollected = true; 27 | public boolean canBeDropped = true; 28 | public int quantity = 1; // How many of the item there are in this "stack" (i.e. you might have hundreds of Gold Pieces in a single "Item") 29 | public int stackLimit = 99999999; 30 | 31 | public Item(GameObject g, String name, int quantity){ 32 | super(g); 33 | owner = g; 34 | this.name = name; 35 | this.id = name; 36 | this.quantity = quantity; 37 | logicFrequency = 0; 38 | } 39 | 40 | public Item(GameObject g){ 41 | this(g, g.getClass().getSimpleName(), 1); 42 | } 43 | 44 | public boolean collect(){ 45 | if (canBeCollected) { 46 | owner.end(); 47 | collectionTimestamp = Bdx.time; 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | public GameObject drop(Scene scene){ 54 | if (canBeDropped) { 55 | GameObject o = scene.add(owner.name); 56 | dropTimestamp = Bdx.time; 57 | if (o.components.get("Item") != null) 58 | o.components.remove("Item"); // Clear whatever item usually "spawns" in with the GameObject 59 | o.components.add(this); // Put yourself back there 60 | owner = o; 61 | return owner; 62 | } 63 | return null; 64 | } 65 | 66 | public String toString(){ 67 | return name + ": " + quantity; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/mesh/AsepriteAnim.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.mesh; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.graphics.GLTexture; 6 | import com.badlogic.gdx.graphics.Mesh; 7 | import com.badlogic.gdx.graphics.Texture; 8 | import com.badlogic.gdx.graphics.g3d.Model; 9 | import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; 10 | import com.badlogic.gdx.math.Matrix3; 11 | import com.badlogic.gdx.utils.JsonReader; 12 | import com.badlogic.gdx.utils.JsonValue; 13 | import com.nilunder.bdx.Bdx; 14 | import com.nilunder.bdx.Component; 15 | import com.nilunder.bdx.GameObject; 16 | import com.nilunder.bdx.State; 17 | import com.nilunder.bdx.utils.Timer; 18 | 19 | import javax.vecmath.Vector2f; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | 24 | /** 25 | * Created by solarlune on 10/26/16. 26 | */ 27 | 28 | public class AsepriteAnim extends Component { 29 | 30 | public static class Frame extends Vector2f { 31 | public int index; 32 | public int sequence; 33 | public float duration; 34 | public Frame(Vector2f v){ 35 | super(v); 36 | } 37 | } 38 | 39 | public static class Animation extends ArrayList{ 40 | 41 | public String name; 42 | public boolean looping; 43 | 44 | public int playHead; 45 | public int playDir; 46 | public Frame current; 47 | 48 | public Animation(String name, boolean looping){ 49 | this.name = name; 50 | this.looping = looping; 51 | playHead = 0; 52 | playDir = 1; 53 | } 54 | 55 | public Frame nextFrame(){ 56 | if (onLastFrame()){ 57 | if (looping) 58 | reset(); 59 | else 60 | playHead -= playDir; 61 | } 62 | 63 | Frame frame = get(playHead); 64 | 65 | current = frame; 66 | 67 | playHead += playDir; 68 | 69 | return frame; 70 | } 71 | 72 | public boolean onLastFrame(){ 73 | return playHead == size() || playHead == -1; 74 | } 75 | 76 | public void reset(){ 77 | if (playDir > 0) 78 | playHead = 0; 79 | else 80 | playHead = size() - 1; 81 | } 82 | 83 | } 84 | 85 | public static class Tag { 86 | 87 | public int start; 88 | public int end; 89 | public String name; 90 | 91 | } 92 | 93 | public float speed; 94 | public HashMap animations; 95 | public Animation active; 96 | public HashMap tags; 97 | 98 | private int prevFrame; 99 | private Timer ticker; 100 | private Matrix3 uvScale; 101 | private boolean rowBased; 102 | private Vector2f baseFrame; 103 | private Vector2f frameDim; 104 | private HashSet prevTouchingTags; 105 | private HashSet touchingTags; 106 | 107 | public AsepriteAnim(GameObject g, int frameWidth, int frameHeight, boolean rowBased, boolean uniqueModel){ 108 | super(g); 109 | if (uniqueModel) 110 | g.mesh(g.mesh().copy()); 111 | this.rowBased = rowBased; 112 | animations = new HashMap(); 113 | ticker = new Timer(); 114 | uvScale = new Matrix3(); 115 | uvScale.idt(); 116 | speed = 1; 117 | state = play; 118 | 119 | // initially displayed frame 120 | HashMap modelToFrame = g.scene.modelToFrame; 121 | 122 | baseFrame = modelToFrame.get(g.modelInstance.model); 123 | if (baseFrame == null){ 124 | baseFrame = uvFrame(); 125 | modelToFrame.put(g.modelInstance.model, baseFrame); 126 | } 127 | 128 | // frameDim 129 | TextureAttribute ta = (TextureAttribute)g.modelInstance.materials.get(0).get(TextureAttribute.Diffuse); 130 | GLTexture t = ta.textureDescription.texture; 131 | float u = 1f / t.getWidth(); 132 | float v = 1f / t.getHeight(); 133 | 134 | frameDim = new Vector2f(u * frameWidth, v * frameHeight); 135 | 136 | tags = new HashMap(); 137 | touchingTags = new HashSet(); 138 | prevTouchingTags = new HashSet(); 139 | } 140 | 141 | public AsepriteAnim(GameObject g, int frameWidth, int frameHeight){ 142 | this(g, frameWidth, frameHeight, true, true); 143 | } 144 | 145 | public void add(String name, int index, int[] frames){ 146 | add(name, index, frames, 12, true); 147 | } 148 | 149 | public void add(String name, int sequence, int[] frames, float fps, boolean looping){ 150 | 151 | Animation anim = new Animation(name, looping); 152 | 153 | for (int i : frames){ 154 | Vector2f f = new Vector2f(baseFrame); 155 | if (rowBased){ 156 | f.x += i * frameDim.x; 157 | f.y += sequence * frameDim.y; 158 | }else{ 159 | f.x += sequence * frameDim.x; 160 | f.y += i * frameDim.y; 161 | } 162 | Frame frame = new Frame(f); 163 | frame.index = i; 164 | frame.sequence = sequence; 165 | frame.duration = 1 / fps; 166 | anim.add(frame); 167 | } 168 | 169 | anim.current = anim.get(0); 170 | 171 | animations.put(anim.name, anim); 172 | 173 | } 174 | 175 | public void load(FileHandle jsonFilePath, boolean importTagsAsAnimations){ 176 | 177 | JsonReader reader = new JsonReader(); 178 | JsonValue data = reader.parse(jsonFilePath); 179 | JsonValue frames = data.get("frames"); 180 | 181 | boolean rowBased = false; 182 | 183 | if (frames.size > 1) { 184 | if (frames.get(1).get("frame").get("y").asFloat() > frames.get(0).get("frame").get("y").asFloat()) 185 | rowBased = true; 186 | } 187 | 188 | for (JsonValue t : data.get("meta").get("frameTags")) { 189 | Tag tag = new Tag(); 190 | tag.name = t.get("name").asString(); 191 | tag.start = t.get("from").asInt(); 192 | tag.end = t.get("to").asInt(); 193 | tags.put(tag.name, tag); 194 | } 195 | 196 | importAnim("All", rowBased, frames, 0, frames.size - 1); 197 | 198 | if (importTagsAsAnimations) { 199 | 200 | for (String tagName : tags.keySet()) 201 | importAnim(tagName, rowBased, frames, tags.get(tagName).start, tags.get(tagName).end); 202 | 203 | } 204 | 205 | } 206 | 207 | public void load(){ 208 | 209 | // Auto-loads using what should be the texture's proposed path 210 | 211 | Texture t = g.mesh().materials.get(0).currentTexture; 212 | String texName = ""; 213 | for (String s : g.scene.textures.keySet()) { 214 | if (g.scene.textures.get(s) == t) { 215 | texName = s; 216 | break; 217 | } 218 | } 219 | 220 | String sub = texName; 221 | 222 | if (texName.lastIndexOf(".") > -1) 223 | sub = texName.substring(0, texName.lastIndexOf(".")); 224 | 225 | FileHandle f = Gdx.files.internal("bdx/textures/" + sub + ".json"); 226 | 227 | load(f, true); 228 | 229 | } 230 | 231 | private Animation importAnim(String animName, boolean rowBased, JsonValue frames, int start, int end){ 232 | 233 | Animation a = new Animation(animName, true); 234 | 235 | for (int i = start; i <= end; i++) { 236 | 237 | JsonValue frameData = frames.get(i); 238 | 239 | Vector2f f = new Vector2f(baseFrame); 240 | 241 | if (rowBased){ 242 | f.y += i * frameDim.y; 243 | }else{ 244 | f.x += i * frameDim.x; 245 | } 246 | 247 | Frame frame = new Frame(f); 248 | frame.index = i; 249 | frame.sequence = 0; 250 | frame.duration = frameData.get("duration").asFloat() / 1000f; 251 | a.add(frame); 252 | 253 | } 254 | 255 | a.current = a.get(0); 256 | 257 | animations.put(a.name, a); 258 | 259 | return a; 260 | 261 | } 262 | 263 | public ArrayList animationNames(){ 264 | return new ArrayList(animations.keySet()); 265 | } 266 | 267 | public void uvScaleX(float s){ 268 | uvScale(s, uvScaleY()); 269 | } 270 | 271 | public void uvScaleY(float s){ 272 | uvScale(uvScaleX(), s); 273 | } 274 | 275 | public float uvScaleX(){ 276 | return uvScale.val[Matrix3.M00]; 277 | } 278 | 279 | public float uvScaleY(){ 280 | return uvScale.val[Matrix3.M11]; 281 | } 282 | 283 | public void play(String name){ 284 | Animation next = animations.get(name); 285 | 286 | if (active != next){ 287 | active = next; 288 | active.playDir = speed < 0 ? -1 : 1; 289 | active.reset(); 290 | ticker.done(true); // immediate play 291 | } 292 | 293 | if (!active.looping && active.onLastFrame()){ 294 | active.playDir = speed < 0 ? -1 : 1; 295 | active.reset(); 296 | ticker.done(true); 297 | } 298 | 299 | } 300 | 301 | public void showNextFrame() { 302 | active.playDir = speed < 0 ? -1 : 1; 303 | uvFrame(active.nextFrame()); 304 | } 305 | 306 | public void frame(int frame){ 307 | active.playHead = frame; // Set the frame, and 308 | ticker.done(true); // Update the sprite immediately 309 | } 310 | 311 | public int frame(){ 312 | return active.playHead - active.playDir; 313 | } 314 | 315 | public boolean frameChanged(){ 316 | return prevFrame != frame(); 317 | } 318 | 319 | private State play = new State(){ 320 | private float nz(float n){ 321 | return n <= 0 ? 0.000001f : n; 322 | } 323 | 324 | public void main(){ 325 | 326 | prevTouchingTags = touchingTags; 327 | touchingTags = touchingTags(); 328 | 329 | if (active == null) 330 | return; 331 | 332 | prevFrame = frame(); 333 | 334 | ticker.interval = nz(Math.abs(active.current.duration) / Math.abs(speed)); 335 | 336 | if (ticker.tick()){ 337 | showNextFrame(); 338 | } 339 | 340 | } 341 | }; 342 | 343 | private void uvFrame(Vector2f frame){ 344 | Matrix3 trans = new Matrix3(); 345 | Vector2f df = uvFrame(); 346 | trans.setToTranslation(frame.x - df.x, frame.y - df.y); 347 | 348 | Mesh mesh = g.modelInstance.model.meshes.first(); 349 | mesh.transformUV(trans); 350 | 351 | } 352 | 353 | private Vector2f uvFrame(){ 354 | Mesh mesh = g.modelInstance.model.meshes.first(); 355 | int n = mesh.getNumVertices(); 356 | float[] verts = new float[n*Bdx.VERT_STRIDE]; 357 | mesh.getVertices(0, verts.length, verts); 358 | 359 | Vector2f frame = new Vector2f(0, 0); 360 | 361 | int uvStart = Bdx.VERT_STRIDE - 2; 362 | for (int v = 0; v < n; ++v){ 363 | int i = v * Bdx.VERT_STRIDE; 364 | frame.x += verts[i + uvStart]; 365 | frame.y += verts[i + uvStart + 1]; 366 | } 367 | 368 | frame.x /= n; 369 | frame.y /= n; 370 | 371 | return frame; 372 | } 373 | 374 | private void scaleUV(Matrix3 scale){ 375 | Matrix3 trans = new Matrix3(); trans.idt(); 376 | Vector2f df = uvFrame(); 377 | trans.setToTranslation(df.x, df.y); 378 | 379 | Matrix3 toOrigin = new Matrix3(trans); 380 | toOrigin.inv(); 381 | 382 | trans.mul(scale); 383 | trans.mul(toOrigin); 384 | 385 | Mesh mesh = g.modelInstance.model.meshes.first(); 386 | mesh.transformUV(trans); 387 | } 388 | 389 | private void uvScale(float x, float y){ 390 | if (uvScaleX() == x && uvScaleY() == y) 391 | return; 392 | 393 | // back to unit scale 394 | uvScale.inv(); 395 | scaleUV(uvScale); 396 | 397 | // set new scale 398 | uvScale.idt(); 399 | uvScale.scale(x, y); 400 | scaleUV(uvScale); 401 | } 402 | 403 | public HashSet touchingTags(){ 404 | HashSet a = new HashSet(); 405 | for (String t : tags.keySet()) { 406 | Tag tag = tags.get(t); 407 | if (tag.start <= active.current.index && active.current.index <= tag.end) 408 | a.add(tag); 409 | 410 | } 411 | return a; 412 | } 413 | 414 | public HashSet hitTags(){ 415 | HashSet result = new HashSet(touchingTags); 416 | result.removeAll(prevTouchingTags); 417 | return result; 418 | } 419 | 420 | public HashSet leftTags(){ 421 | HashSet result = new HashSet(prevTouchingTags); 422 | result.removeAll(touchingTags); 423 | return result; 424 | } 425 | 426 | } 427 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/mesh/Trail.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.mesh; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | 7 | import javax.vecmath.Matrix3f; 8 | import javax.vecmath.Vector3f; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.Comparator; 12 | import java.util.HashMap; 13 | 14 | /** 15 | * Created by solarlune on 5/10/16. 16 | */ 17 | public class Trail extends Component { 18 | 19 | private class VertexCollection { 20 | 21 | public float position; 22 | public ArrayList indices; 23 | 24 | public VertexCollection(float position){ 25 | this.position = position; 26 | indices = new ArrayList(); 27 | } 28 | 29 | } 30 | 31 | private class PosOriPair { 32 | 33 | Vector3f position; 34 | Matrix3f orientation; 35 | 36 | public PosOriPair(Vector3f position, Matrix3f orientation){ 37 | this.position = position; 38 | this.orientation = orientation; 39 | } 40 | 41 | public String toString(){ 42 | return position.toString(); 43 | } 44 | 45 | } 46 | 47 | HashMap vertOffsets; 48 | ArrayList vertPairs; 49 | ArrayList targetPositions; 50 | 51 | public boolean stretch = false; // If the trail should stretch (difference between a lightcycle trail and a cape) 52 | public int spacing = 3; // Number of frames between the movement and the vertex update 53 | public boolean reverse = false; // If the trail should reverse its movements (i.e. when a sword swings left, the trail swings left too) 54 | public String axis = "y"; // The axis the vertices align on 55 | public boolean updateOnlyWhenMoving = false; // If the trail should only update if its owning object moves 56 | public GameObject target = null; // What object the trail follows. The trail should not be parented to this object. 57 | public int materialIndex = 0; 58 | 59 | Vector3f targetPastPos = new Vector3f(); 60 | Matrix3f targetPastOri = new Matrix3f(); 61 | 62 | public Trail(GameObject g, GameObject target, String axis){ 63 | super(g); 64 | this.axis = axis; 65 | this.target = target; 66 | state(main); 67 | 68 | } 69 | 70 | State main = new State(){ 71 | 72 | public void enter(){ 73 | 74 | vertOffsets = new HashMap(); 75 | 76 | for (int i = 0; i < g.mesh().getVertexCount(materialIndex); i++) 77 | vertOffsets.put(i, g.mesh().vertPos(materialIndex, i)); 78 | 79 | vertPairs = new ArrayList(); 80 | 81 | HashMap> vp = new HashMap>(); 82 | 83 | for (int i = 0; i < g.mesh().getVertexCount(materialIndex); i++) { 84 | float vertPos; 85 | if (axis.toLowerCase().equals("x")) 86 | vertPos = Math.round(g.mesh().vertPos(materialIndex, i).x * 100.0f) / 100.0f; 87 | else if (axis.toLowerCase().equals("y")) 88 | vertPos = Math.round(g.mesh().vertPos(materialIndex, i).y * 100.0f) / 100.0f; 89 | else 90 | vertPos = Math.round(g.mesh().vertPos(materialIndex, i).z * 100.0f) / 100.0f; 91 | 92 | if (!vp.containsKey(vertPos)) 93 | vp.put(vertPos, new ArrayList()); 94 | 95 | vp.get(vertPos).add(i); 96 | 97 | } 98 | 99 | for (float axisValue : vp.keySet()){ 100 | VertexCollection ax = new VertexCollection(axisValue); 101 | ax.indices.addAll(vp.get(axisValue)); 102 | vertPairs.add(ax); 103 | } 104 | 105 | vertPairs.sort(new Comparator() { 106 | @Override 107 | public int compare(VertexCollection o1, VertexCollection o2) { 108 | if (o1.position > o2.position) 109 | return -1; 110 | else 111 | return 1; 112 | } 113 | }); 114 | 115 | Collections.reverse(vertPairs); 116 | 117 | targetPositions = new ArrayList(); 118 | 119 | for (int x = 0; x < vertPairs.size() * spacing; x++) 120 | targetPositions.add(new PosOriPair(g.position(), g.orientation())); 121 | 122 | } 123 | 124 | public void main(){ 125 | 126 | if (g.parent() != target) { 127 | g.position(target.position()); 128 | g.orientation(target.orientation()); 129 | } 130 | 131 | boolean insert = false; 132 | 133 | if (!updateOnlyWhenMoving) 134 | insert = true; 135 | else { 136 | float posDiff = target.position().minus(targetPastPos).length(); 137 | Matrix3f ori = target.orientation(); 138 | ori.sub(targetPastOri); 139 | float oriDiff = ori.getScale(); 140 | float thresh = 0.0001f; 141 | if (posDiff > thresh || oriDiff > thresh) 142 | insert = true; 143 | } 144 | 145 | if (insert) 146 | targetPositions.add(new PosOriPair(new Vector3f(target.position()), new Matrix3f(target.orientation()))); 147 | 148 | if (targetPositions.size() > vertPairs.size() * spacing) 149 | targetPositions.remove(0); 150 | 151 | Vector3f diff; 152 | Matrix3f invTargetOri = new Matrix3f(target.orientation()); 153 | invTargetOri.invert(); 154 | 155 | for (int x = 0; x < vertPairs.size(); x++) { 156 | 157 | ArrayList verts = vertPairs.get(x).indices; 158 | 159 | if (targetPositions.size() > x * spacing){ 160 | 161 | Vector3f pos = targetPositions.get(x * spacing).position; 162 | Matrix3f ori = targetPositions.get(x * spacing).orientation; 163 | Matrix3f oriInverted = new Matrix3f(ori); 164 | oriInverted.invert(); 165 | 166 | for (int v : verts) { 167 | 168 | if (!reverse) { 169 | 170 | if (stretch) { 171 | diff = ori.mult(pos.minus(target.position())); 172 | g.mesh().vertPos(materialIndex, v, target.orientation().mult(vertOffsets.get(v).plus(diff))); 173 | } 174 | else 175 | g.mesh().vertPos(materialIndex, v, target.orientation().mult(vertOffsets.get(v))); 176 | 177 | Vector3f vPos = g.mesh().vertPos(materialIndex, v); 178 | g.mesh().vertPos(materialIndex, v, oriInverted.mult(vPos)); 179 | 180 | } 181 | else { 182 | 183 | if (stretch) { 184 | diff = ori.mult(pos.minus(target.position())); 185 | g.mesh().vertPos(materialIndex, v, invTargetOri.mult(vertOffsets.get(v).plus(diff))); 186 | } 187 | else 188 | g.mesh().vertPos(materialIndex, v, invTargetOri.mult(vertOffsets.get(v))); 189 | 190 | Vector3f vPos = g.mesh().vertPos(materialIndex, v); 191 | g.mesh().vertPos(materialIndex, v, ori.mult(vPos)); 192 | 193 | } 194 | 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | targetPastOri = new Matrix3f(target.orientation()); 202 | targetPastPos = new Vector3f(target.position()); 203 | 204 | } 205 | 206 | }; 207 | 208 | 209 | 210 | } 211 | 212 | 213 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/movement/ConstantMotion.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.movement; 2 | 3 | import com.nilunder.bdx.Bdx; 4 | import com.nilunder.bdx.Component; 5 | import com.nilunder.bdx.GameObject; 6 | import com.nilunder.bdx.State; 7 | 8 | import javax.vecmath.Vector3f; 9 | 10 | /** 11 | * Created by solarlune on 5/7/16. 12 | */ 13 | public class ConstantMotion extends Component { 14 | 15 | Vector3f motion = new Vector3f(); 16 | 17 | public ConstantMotion (GameObject g, Vector3f motion) { 18 | super(g); 19 | state = main; 20 | this.motion.set(motion); 21 | } 22 | 23 | State main = new State(){ 24 | 25 | public void main(){ 26 | g.move(motion.mul(Bdx.TICK_TIME)); 27 | } 28 | 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/movement/GroundRay.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.movement; 2 | 3 | import java.util.ArrayList; 4 | 5 | import javax.vecmath.Vector3f; 6 | 7 | import com.nilunder.bdx.Bdx; 8 | import com.nilunder.bdx.Component; 9 | import com.nilunder.bdx.GameObject; 10 | import com.nilunder.bdx.RayHit; 11 | import com.nilunder.bdx.State; 12 | import com.nilunder.bdx.utils.Color; 13 | import com.nilunder.bdx.utils.Timer; 14 | 15 | public class GroundRay extends Component { 16 | 17 | class RayResults { 18 | ArrayList results = new ArrayList(); 19 | Vector3f offset = new Vector3f(); 20 | Vector3f startPos = new Vector3f(); 21 | } 22 | 23 | public float groundOffset; 24 | public float rayExtraDistance; 25 | public float weight = 1; 26 | public boolean snapToGround = true; 27 | public boolean alignToGround = false; 28 | public ArrayList ignoredProperties = new ArrayList(); 29 | public ArrayList checkedProperties = new ArrayList(); 30 | public ArrayList castPoints = new ArrayList(); 31 | public Vector3f down = new Vector3f(0, 0, -1); 32 | public float minimumEscapeSpeed = 1; 33 | public boolean debugOn = false; 34 | public boolean prioritizeFurthest = false; 35 | 36 | public GameObject hitObject; 37 | public GameObject lastHitObject; 38 | public Vector3f hitPosition; 39 | public Vector3f lastHitPosition; 40 | public Vector3f hitNormal; 41 | public Vector3f lastHitNormal; 42 | public Timer offGroundTimer = new Timer(0); // How long before the object is officially not on the ground 43 | 44 | public GroundRay(GameObject g) { 45 | this(g, 0.05f, 0.05f); 46 | } 47 | 48 | public GroundRay(GameObject g, float rayExtra, float groundOffset) { 49 | super(g); 50 | state = main; 51 | this.rayExtraDistance = rayExtra; 52 | this.groundOffset = groundOffset; 53 | ignoredProperties.add("notGround"); 54 | } 55 | 56 | State main = new State(){ 57 | 58 | public void main() { 59 | 60 | boolean childCasters = false; 61 | 62 | lastHitObject = hitObject; 63 | lastHitNormal = hitNormal; 64 | lastHitPosition = hitPosition; 65 | 66 | Vector3f size = g.dimensions(); 67 | 68 | float height = size.z / 2; 69 | 70 | float rayDist = rayExtraDistance + (Math.abs(g.velocityLocal().z) * Bdx.TICK_TIME); 71 | 72 | ArrayList allResults = new ArrayList(); 73 | 74 | if (castPoints.size() == 0) { 75 | RayResults r = new RayResults(); 76 | r.startPos = g.position().plus(down.mul(height)); 77 | r.results.addAll(g.scene.xray(g.position().plus(down.mul(height)), down.mul(rayDist))); 78 | allResults.add(r); 79 | } 80 | else { 81 | childCasters = true; 82 | for (GameObject castPoint : castPoints){ 83 | RayResults r = new RayResults(); 84 | r.startPos = castPoint.position(); 85 | r.results.addAll(g.scene.xray(castPoint.position(), down.mul(rayDist))); 86 | r.offset = g.position().minus(castPoint.position()); 87 | allResults.add(r); 88 | } 89 | } 90 | 91 | g.alignAxisToVec(2, down.negated()); 92 | 93 | if (offGroundTimer.done()) { 94 | hitPosition = null; 95 | hitObject = null; 96 | hitNormal = null; 97 | } 98 | 99 | float prevDist = -1; 100 | 101 | for (RayResults result : allResults) { 102 | 103 | if (debugOn) 104 | g.scene.drawLine(result.startPos, result.startPos.plus(down.mul(rayDist)), new Color(1, 0, 0, 1)); 105 | 106 | for (RayHit ray : result.results) { 107 | 108 | boolean skip = false; 109 | 110 | if (checkedProperties.size() > 0) { 111 | skip = true; 112 | for (String propName : checkedProperties) 113 | if (ray.object.props.containsKey(propName)) 114 | skip = false; 115 | } 116 | for (String propName : ignoredProperties){ 117 | if (ray.object.props.containsKey(propName)) 118 | skip = true; 119 | } 120 | if (ray.object == g) 121 | skip = true; 122 | 123 | if (prevDist >= 0 && ray.position.minus(result.startPos).length() < prevDist) 124 | skip = true; 125 | 126 | if (skip) 127 | continue; 128 | 129 | // Success! 130 | 131 | if (debugOn) 132 | g.scene.drawLine(result.startPos, result.startPos.plus(down), new Color(0, 1, 0, 1)); 133 | 134 | if (g.velocityLocal().z < minimumEscapeSpeed) { 135 | 136 | hitObject = ray.object; 137 | hitPosition = ray.position; 138 | hitNormal = ray.normal; 139 | prevDist = ray.position.minus(result.startPos).length(); 140 | offGroundTimer.restart(); 141 | 142 | Vector3f relativePos = hitPosition.minus(hitObject.position()); 143 | 144 | float w = weight * g.mass(); 145 | 146 | if (justLanded()) 147 | w *= Math.abs(g.velocityLocal().z); 148 | 149 | hitObject.applyForce(g.scene.gravity().mul(g.mass() * w), relativePos); 150 | 151 | if (alignToGround) 152 | g.alignAxisToVec(2, ray.normal); 153 | 154 | if (snapToGround) { 155 | g.position(ray.position); 156 | if (childCasters) { 157 | g.move(result.offset); 158 | g.move(down.negated().mul(groundOffset)); 159 | } else 160 | g.move(down.negated().mul(height + groundOffset)); 161 | Vector3f vel = g.velocityLocal(); 162 | vel.z = 0; 163 | g.velocityLocal(vel); 164 | } 165 | 166 | } 167 | 168 | } 169 | 170 | if (hitObject != null) 171 | break; 172 | 173 | } 174 | 175 | } 176 | 177 | }; 178 | 179 | public boolean justLanded(){ 180 | return hitObject != null && lastHitObject == null && g.velocityLocal().z < minimumEscapeSpeed; 181 | } 182 | 183 | public boolean justJumped(){ 184 | return hitObject == null && lastHitObject != null; 185 | } 186 | 187 | public boolean onGround(){ 188 | return hitObject != null; 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/movement/LockMotion.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.movement; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | 7 | import javax.vecmath.Vector3f; 8 | 9 | /** 10 | * Created by solarlune on 7/13/16. 11 | */ 12 | public class LockMotion extends Component { 13 | 14 | public String axes; 15 | public Vector3f lockPosition = new Vector3f(); 16 | public Vector3f lockVelocity = new Vector3f(); 17 | 18 | public LockMotion(GameObject g, String axes) { 19 | super(g); 20 | this.axes = axes; 21 | state = mainState; 22 | } 23 | 24 | public LockMotion(GameObject g) { 25 | this(g, "y"); 26 | } 27 | 28 | State mainState = new State() { 29 | 30 | public void main() { 31 | 32 | Vector3f vec = g.velocity(); 33 | if (axes.toLowerCase().contains("x")) 34 | vec.x = lockVelocity.x; 35 | if (axes.toLowerCase().contains("y")) 36 | vec.y = lockVelocity.y; 37 | if (axes.toLowerCase().contains("z")) 38 | vec.z = lockVelocity.z; 39 | g.velocity(vec); 40 | 41 | vec = g.position(); 42 | if (axes.toLowerCase().contains("x")) 43 | vec.x = lockPosition.x; 44 | if (axes.toLowerCase().contains("y")) 45 | vec.y = lockPosition.y; 46 | if (axes.toLowerCase().contains("z")) 47 | vec.z = lockPosition.z; 48 | g.position(vec); 49 | } 50 | 51 | }; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/movement/LockToGridComponent.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.movement; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | 7 | import javax.vecmath.Vector3f; 8 | 9 | /** 10 | * Created by solarlune on 7/30/16. 11 | */ 12 | public class LockToGridComponent extends Component { 13 | 14 | public GameObject parent; 15 | public float gridSize = 1; 16 | 17 | public LockToGridComponent(GameObject g) { 18 | super(g); 19 | state(mainState); 20 | } 21 | 22 | State mainState = new State(){ 23 | 24 | public void main(){ 25 | 26 | Vector3f pos; 27 | 28 | if (parent != null) 29 | pos = parent.position(); 30 | else 31 | pos = g.position(); 32 | 33 | pos.x = Math.round(pos.x * gridSize) / gridSize; 34 | pos.y = Math.round(pos.y * gridSize) / gridSize; 35 | pos.z = Math.round(pos.z * gridSize) / gridSize; 36 | g.position(pos); 37 | } 38 | 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/movement/SnapAxis.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.movement; 2 | 3 | import javax.vecmath.Vector3f; 4 | 5 | import com.nilunder.bdx.Component; 6 | import com.nilunder.bdx.GameObject; 7 | import com.nilunder.bdx.State; 8 | 9 | public class SnapAxis extends Component { 10 | 11 | int axis; 12 | float snapTo; 13 | 14 | public SnapAxis(GameObject g, int axis, float snapTo) { 15 | super(g); 16 | state = mainState; 17 | this.axis = axis; 18 | this.snapTo = snapTo; 19 | } 20 | 21 | State mainState = new State(){ 22 | 23 | public void main(){ 24 | 25 | Vector3f pos = g.position(); 26 | 27 | if (axis == 0) 28 | pos.x = snapTo; 29 | else if (axis == 1) 30 | pos.y = snapTo; 31 | else 32 | pos.z = snapTo; 33 | 34 | g.position(pos); 35 | 36 | } 37 | 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/particles/Emitter.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.particles; 2 | 3 | import java.util.ArrayList; 4 | 5 | import javax.vecmath.Vector3f; 6 | import javax.vecmath.Vector4f; 7 | 8 | import com.nilunder.bdx.Component; 9 | import com.nilunder.bdx.GameObject; 10 | import com.nilunder.bdx.State; 11 | import com.nilunder.bdx.components.Halo; 12 | import com.nilunder.bdx.utils.Color; 13 | import com.nilunder.bdx.utils.Random; 14 | import com.nilunder.bdx.utils.Timer; 15 | 16 | public class Emitter extends Component { 17 | 18 | public ArrayList particles; 19 | private ArrayList particleTemplates; // Names of particles that are randomly chosen for the particles to spawn 20 | public Vector3f windDirection; // Wind (constant force) direction 21 | public Vector3f gravityDirection; // Gravity (additive force) direction 22 | public Vector3f spawnOffset; // Spawn position offset 23 | public Vector3f spawnRandom; // Random range in which particles can spawn 24 | public Vector3f startingVelocity; // Starting velocity for particles 25 | public Vector3f randomVelocity; // Random velocity for particles 26 | public float friction; // Linear friction applied as particles move 27 | private float spawnWait; // In seconds 28 | public float minLife; // Minimum lifetime for particles in seconds 29 | public float maxLife; // Max lifetime 30 | public float minSize; // Minimum size for particles (scaling is uniform) 31 | public float maxSize; // Maximum size 32 | public int spawnNum; // Number of particles to spawn at a time 33 | public int maxNumParticles; // Maximum number of particles that can exist 34 | public boolean halo; // If the particles should face the camera 35 | 36 | public ArrayList colorStages; // Color stages 37 | 38 | private Timer spawnTimer; 39 | 40 | public Emitter(GameObject g) { 41 | 42 | super(g); 43 | 44 | particles = new ArrayList(); 45 | particleTemplates = new ArrayList(); 46 | 47 | windDirection = new Vector3f(); 48 | gravityDirection = new Vector3f(); 49 | spawnOffset = new Vector3f(); 50 | spawnRandom = new Vector3f(); 51 | startingVelocity = new Vector3f(); 52 | randomVelocity = new Vector3f(); 53 | 54 | colorStages = new ArrayList(); 55 | 56 | friction = 0; 57 | minLife = 1; 58 | maxLife = 1; 59 | minSize = 1; 60 | maxSize = 1; 61 | maxNumParticles = Integer.MAX_VALUE; 62 | spawnNum = 1; 63 | halo = true; 64 | 65 | spawnTimer = new Timer(); 66 | spawnTime(0.1f); 67 | 68 | state = mainState; 69 | 70 | } 71 | 72 | public void addTemplate(String partName){ 73 | particleTemplates.add(partName); 74 | } 75 | 76 | State mainState = new State(){ 77 | 78 | public void main(){ 79 | 80 | if (spawnTimer.done() && !spawnTimer.paused()) { 81 | spawnTimer.restart(); 82 | spawn(spawnNum); 83 | } 84 | 85 | } 86 | 87 | }; 88 | 89 | public ArrayList spawn(int spawnNumber) { 90 | 91 | ArrayList spawned = new ArrayList(); 92 | 93 | for (int i = 0; i < spawnNumber; i++) { 94 | 95 | if ((particleTemplates.size() > 0) && (particles.size() < maxNumParticles)) { 96 | 97 | String choice = Random.choice(particleTemplates); 98 | 99 | GameObject part = g.scene.add(choice); 100 | 101 | spawned.add(part); 102 | 103 | part.position(g.position().plus(spawnOffset)); 104 | part.move(Random.direction().mul(spawnRandom)); 105 | if (halo) 106 | part.components.add(new Halo(part)); 107 | part.scale(Random.random(minSize, maxSize)); 108 | 109 | Particle partComp = new Particle(part, this, Random.random(minLife, maxLife)); 110 | partComp.velocity = new Vector3f(startingVelocity); 111 | partComp.velocity.add(Random.direction().mul(randomVelocity)); 112 | part.components.add(partComp); 113 | 114 | particles.add(part); 115 | 116 | } 117 | 118 | } 119 | 120 | return spawned; 121 | 122 | } 123 | 124 | public ArrayList spawn(){ 125 | return spawn(1); 126 | } 127 | 128 | public void spawnTime(float spawnTime){ 129 | spawnWait = spawnTime; 130 | if (spawnWait > 0) { 131 | spawnTimer.set(spawnWait); 132 | spawnTimer.resume(); 133 | spawnTimer.restart(); 134 | } 135 | else 136 | spawnTimer.pause(); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/particles/Particle.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.particles; 2 | 3 | import javax.vecmath.Vector3f; 4 | 5 | import com.nilunder.bdx.Bdx; 6 | import com.nilunder.bdx.Component; 7 | import com.nilunder.bdx.GameObject; 8 | import com.nilunder.bdx.State; 9 | import com.nilunder.bdx.utils.Color; 10 | import com.nilunder.bdx.utils.Timer; 11 | import com.solarlune.bdxhelper.utils.BDXHMath; 12 | 13 | public class Particle extends Component { 14 | 15 | public Emitter system; 16 | 17 | public Vector3f velocity; 18 | 19 | public float lifeTime; 20 | 21 | private Timer lifeTimer; 22 | 23 | public Particle(GameObject g, Emitter s, float lifeTime) { 24 | super(g); 25 | system = s; 26 | state = mainState; 27 | this.lifeTime = lifeTime; 28 | lifeTimer = new Timer(lifeTime); 29 | velocity = new Vector3f(); 30 | } 31 | 32 | State mainState = new State(){ 33 | 34 | public void main(){ 35 | 36 | velocity.add(system.gravityDirection.mul(Bdx.TICK_TIME)); 37 | 38 | if (velocity.length() > system.friction) { 39 | Vector3f neg = velocity.negated(); 40 | velocity.add(neg.mul(system.friction)); 41 | } 42 | else 43 | velocity.length(0); 44 | 45 | Vector3f vel = new Vector3f(velocity); 46 | vel.add(system.windDirection); 47 | 48 | g.move(vel.mul(Bdx.TICK_TIME)); 49 | 50 | if (lifeTimer.done()) { 51 | g.end(); 52 | system.particles.remove(g); 53 | } 54 | else { 55 | 56 | float lifePercent = (lifeTime - lifeTimer.timeLeft()) / lifeTime; 57 | 58 | if (system.colorStages.size() > 0) { 59 | 60 | int stageNum = system.colorStages.size() - 1; 61 | int currentStage = java.lang.Math.min((int) java.lang.Math.floor(stageNum * lifePercent), system.colorStages.size() - 1); 62 | 63 | Color currentColor = system.colorStages.get(currentStage); 64 | Color nextColor = currentColor; 65 | 66 | float stagePercent = (lifePercent * stageNum) - currentStage; 67 | 68 | if (system.colorStages.size() > currentStage + 1) 69 | nextColor = system.colorStages.get(currentStage + 1); 70 | 71 | Color c = new Color(); 72 | c.a = BDXHMath.lerp(currentColor.a, nextColor.a, stagePercent); 73 | c.r = BDXHMath.lerp(currentColor.r, nextColor.r, stagePercent); 74 | c.g = BDXHMath.lerp(currentColor.g, nextColor.g, stagePercent); 75 | c.b = BDXHMath.lerp(currentColor.b, nextColor.b, stagePercent); 76 | 77 | g.mesh().materials.color(c); 78 | 79 | } 80 | 81 | } 82 | 83 | } 84 | 85 | }; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/properties/Gauge.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.properties; 2 | 3 | import com.nilunder.bdx.Bdx; 4 | import com.nilunder.bdx.Component; 5 | import com.nilunder.bdx.GameObject; 6 | import com.nilunder.bdx.State; 7 | 8 | public class Gauge extends Component { 9 | 10 | public enum Adjust { // What happens if you adjust the maximum value (e.g. when VAL = 5 and MAX was 10, but MAX changes to 20) 11 | NONE, // Don't do anything (M = 20, V = 5) 12 | RATIO, // Set value to maintain ratio (M = 20, V = 10) 13 | VALUE, // Set value to maintain value difference (M = 20, V = 15) 14 | REFILL, // Set value to match the new maximum (M = 20, V = 20) 15 | } 16 | 17 | public enum Regen { 18 | VALUE, // Regen value per second 19 | PERCENTAGE, // Regen percentage of max per second 20 | } 21 | 22 | private float value; 23 | private float maxValue; 24 | public boolean invulnerable = false; 25 | public float regenRate = 0; // Regeneration per second 26 | public Adjust onMaxAdjust = Adjust.NONE; // What to do when the max is adjusted 27 | public Regen regenMode = Regen.PERCENTAGE; // Regeneration mode 28 | public boolean allowNegatives = false; 29 | public boolean bottomedOut = false; 30 | private boolean prevBottomedOut = false; 31 | public float bottomOutRelease = 0.2f; // What percentage to release regen penalty when bottomed out (if atBottom is glow to CUTREGEN) 32 | public float bottomOutRegenCut = 0.5f; // What percentage to cut regen by when bottomed out 33 | public float regenBoostOnPercentage = 0.0f; // How much regen boost to give according to the percentage remaining 34 | 35 | public Gauge(GameObject g, float value, String name) { 36 | super(g); 37 | max(value); 38 | value(value); 39 | state = stateMain; 40 | this.name = name; 41 | } 42 | 43 | public Gauge(GameObject g) { 44 | this(g, 10, "Gauge"); 45 | } 46 | 47 | public void add(float value){ 48 | value(value() + value); 49 | } 50 | 51 | public void sub(float value){ 52 | if (!invulnerable) 53 | value(value() - value); 54 | } 55 | 56 | public void mul(float value) { 57 | value(value() * value); 58 | } 59 | 60 | public void div(float value) { 61 | value(value() / value); 62 | } 63 | 64 | public void value(float value){ 65 | if (allowNegatives) 66 | this.value = Math.min(value, max()); 67 | else 68 | this.value = Math.max(Math.min(value, max()), 0); 69 | } 70 | 71 | public float value() { 72 | return value; 73 | } 74 | 75 | public float valueAsPercentage(){ 76 | return value() / max(); 77 | } 78 | 79 | public void max(float value) { 80 | readjustStrat(value); 81 | this.maxValue = value; 82 | } 83 | 84 | public float max(){ 85 | return maxValue; 86 | } 87 | 88 | private void readjustStrat(float newMax){ 89 | if (onMaxAdjust == Adjust.RATIO){ 90 | float ratio = newMax / maxValue; 91 | value *= ratio; 92 | } 93 | else if (onMaxAdjust == Adjust.VALUE) { 94 | float diff = newMax - maxValue; 95 | value += diff; 96 | } 97 | else if (onMaxAdjust == Adjust.REFILL) 98 | value = newMax; 99 | } 100 | 101 | public boolean justEmptied(){ 102 | return bottomedOut && !prevBottomedOut; 103 | } 104 | 105 | State stateMain = new State(){ 106 | 107 | public void main() { 108 | 109 | prevBottomedOut = bottomedOut; 110 | 111 | float regen = 0; 112 | 113 | if (regenRate != 0) { 114 | if (regenMode == Regen.PERCENTAGE) 115 | regen = (maxValue / regenRate) * Bdx.TICK_TIME; 116 | else 117 | regen = regenRate * Bdx.TICK_TIME; 118 | } 119 | 120 | regen += regenBoostOnPercentage * valueAsPercentage() * Bdx.TICK_TIME; 121 | 122 | if (value() <= 0) 123 | bottomedOut = true; 124 | 125 | if (valueAsPercentage() >= bottomOutRelease) 126 | bottomedOut = false; 127 | 128 | if (bottomedOut) 129 | regen *= bottomOutRegenCut; 130 | 131 | add(regen); 132 | 133 | }; 134 | 135 | }; 136 | 137 | } 138 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/properties/RoomIdentifier2D.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.properties; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.RayHit; 6 | import com.nilunder.bdx.State; 7 | 8 | import javax.vecmath.Vector3f; 9 | 10 | public class RoomIdentifier2D extends Component { 11 | 12 | public GameObject currentRoom; 13 | public GameObject pastRoom; 14 | 15 | public RoomIdentifier2D(GameObject g) { 16 | super(g); 17 | state = mainState; 18 | } 19 | 20 | State mainState = new State(){ 21 | 22 | public void main(){ 23 | check(); 24 | } 25 | 26 | }; 27 | 28 | public void check(){ 29 | pastRoom = currentRoom; 30 | 31 | RayHit ray = g.scene.ray(g.position().plus(new Vector3f(0, -50, 0)), new Vector3f(0, 100, 0)); 32 | 33 | if (ray != null && ray.object.props.containsKey("room")) 34 | currentRoom = ray.object; 35 | } 36 | 37 | public boolean changedRooms(){ 38 | return currentRoom != pastRoom; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/properties/TeamComponent.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.properties; 2 | 3 | import com.badlogic.gdx.utils.JsonValue; 4 | import com.nilunder.bdx.Component; 5 | import com.nilunder.bdx.GameObject; 6 | import com.nilunder.bdx.State; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | 11 | /** 12 | * Created by solarlune on 8/2/16. 13 | */ 14 | 15 | public class TeamComponent extends Component { 16 | 17 | static HashMap> teamCollection; 18 | 19 | String team; 20 | 21 | public TeamComponent(GameObject g, String team){ 22 | super(g); 23 | this.team = team; 24 | if (teamCollection == null) 25 | teamCollection = new HashMap>(); 26 | } 27 | 28 | public State mainState = new State(){ 29 | 30 | public void enter() { 31 | if (!teamCollection.containsKey(team)) 32 | teamCollection.put(team, new ArrayList()); 33 | teamCollection.get(team).add(g); 34 | g.props.put("team-"+team, new JsonValue(true)); 35 | } 36 | 37 | public void exit() { 38 | teamCollection.get(team).remove(g); 39 | g.props.remove("team-"+team); 40 | } 41 | }; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/properties/TimedEnd.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.properties; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | import com.nilunder.bdx.utils.Timer; 7 | 8 | /** 9 | * Created by SolarLuneNew on 3/23/2016. 10 | */ 11 | public class TimedEnd extends Component { 12 | 13 | public Timer timer; 14 | 15 | public TimedEnd(GameObject g, float time){ 16 | super(g); 17 | timer = new Timer(time); 18 | state = main; 19 | } 20 | 21 | State main = new State(){ 22 | public void main(){ 23 | if (timer.done()) 24 | g.end(); 25 | } 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/components/properties/TouchDamage.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.components.properties; 2 | 3 | import com.nilunder.bdx.Component; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.State; 6 | 7 | public class TouchDamage extends Component { 8 | 9 | String targetType; 10 | float attack; 11 | 12 | public TouchDamage(GameObject g, String t, float attack) { 13 | super(g); 14 | targetType = t; 15 | this.attack = attack; 16 | state = mainState; 17 | } 18 | 19 | State mainState = new State(){ 20 | 21 | public void main(){ 22 | 23 | for (GameObject other : g.touchingObjects) { 24 | 25 | if (targetType == null || other.props.containsKey(targetType)) { 26 | 27 | Gauge health = (Gauge) other.components.get("Health"); 28 | 29 | if (health != null) 30 | health.sub(attack); 31 | 32 | } 33 | 34 | } 35 | 36 | } 37 | 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/input/GamepadProfiles.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.input; 2 | 3 | import com.nilunder.bdx.inputs.Gamepad; 4 | import com.nilunder.bdx.inputs.Gamepad.Axis; 5 | import com.nilunder.bdx.inputs.Gamepad.Profile; 6 | 7 | import java.util.HashMap; 8 | 9 | public class GamepadProfiles { 10 | 11 | static public Profile SNES(){ 12 | 13 | Profile p = new Profile("SNES"); 14 | 15 | p.btnToCode.put("x", 0); 16 | p.btnToCode.put("y", 3); 17 | p.btnToCode.put("a", 1); 18 | p.btnToCode.put("b", 2); 19 | p.btnToCode.put("l", 4); 20 | p.btnToCode.put("r", 5); 21 | 22 | if (System.getProperty("os.name").startsWith("Windows")) { 23 | p.axes.put("lx", new Axis(1)); 24 | p.axes.put("ly", new Axis(0)); 25 | p.btnToCode.put("start", 10); 26 | p.btnToCode.put("select", 9); 27 | } 28 | else { 29 | p.axes.put("lx", new Axis(0)); // Linux is different compared to Windows 30 | p.axes.put("ly", new Axis(1)); 31 | p.btnToCode.put("start", 9); 32 | p.btnToCode.put("select", 8); 33 | } 34 | 35 | p.btnToCode.put("left", -200 - p.axes.get("lx").code); 36 | p.btnToCode.put("right", 200 + p.axes.get("lx").code); 37 | p.btnToCode.put("up", -200 - p.axes.get("ly").code); 38 | p.btnToCode.put("down", 200 + p.axes.get("ly").code); 39 | 40 | return p; 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/randgen/Cell.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.randgen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | 6 | import javax.vecmath.Vector2f; 7 | 8 | import com.nilunder.bdx.GameObject; 9 | 10 | public class Cell { 11 | 12 | private Vector2f position; 13 | public ArrayList connections; 14 | public ArrayList connectionsInitiated; 15 | public Map map; 16 | private String value; 17 | public GameObject createdRoom; 18 | 19 | public Cell(int x, int y, Map map){ 20 | position = new Vector2f(x, y); 21 | connections = new ArrayList(); 22 | connectionsInitiated = new ArrayList(); 23 | setValue("0"); 24 | this.map = map; 25 | } 26 | 27 | public Cell(Vector2f coords, Map map) { 28 | this((int) map.getCoords(coords).x, (int) map.getCoords(coords).y, map); 29 | } 30 | 31 | public void connect(Cell other, String value){ 32 | 33 | if (other != this) { 34 | 35 | map.set(position, value); 36 | map.set(other.position, value); 37 | connections.add(other); 38 | other.connections.add(this); 39 | connectionsInitiated.add(other); 40 | 41 | } 42 | 43 | } 44 | 45 | public HashMap getNeighbors(){ 46 | 47 | HashMap hash = new HashMap(); 48 | 49 | for (Cell conn : connections) { 50 | 51 | if (conn.position.x > position.x + 0.01f) 52 | hash.put("right", conn); 53 | else if (conn.position.x < position.x - 0.01f) 54 | hash.put("left", conn); 55 | else if (conn.position.y > position.y + 0.01f) 56 | hash.put("down", conn); 57 | else if (conn.position.y < position.y - 0.01f) 58 | hash.put("up", conn); 59 | 60 | } 61 | 62 | return hash; 63 | 64 | } 65 | 66 | public void setValue(String value){ 67 | this.value = value; 68 | } 69 | 70 | public String getValue(){ 71 | return value; 72 | } 73 | 74 | public String toString(){ 75 | return getValue(); 76 | } 77 | 78 | public Vector2f position(){ 79 | return new Vector2f(position); 80 | } 81 | 82 | public void position(Vector2f pos){ 83 | position = pos; 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/randgen/Map.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.randgen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | 7 | import javax.vecmath.Vector2f; 8 | import javax.vecmath.Vector3f; 9 | 10 | import com.nilunder.bdx.GameObject; 11 | import com.nilunder.bdx.Scene; 12 | import com.nilunder.bdx.utils.Random; 13 | import com.solarlune.bdxhelper.utils.BDXHMath; 14 | 15 | import java.lang.Math; 16 | 17 | public class Map extends ArrayList> { 18 | 19 | public enum Wrap { 20 | 21 | NONE, 22 | CLAMP, 23 | WRAP; 24 | 25 | } 26 | 27 | public enum ConnectionMode { // Used for generateNodes algorithm 28 | 29 | ALL, 30 | HUB, 31 | ONE, 32 | 33 | } 34 | 35 | public Wrap wrapBehavior = Wrap.CLAMP; 36 | 37 | public ArrayList openCells = new ArrayList(); 38 | 39 | public long seed = 0; 40 | 41 | public String EMPTY_CELL = " "; 42 | 43 | public Scene scene; 44 | 45 | int sizeX, sizeY; 46 | 47 | public Map(int sizeX, int sizeY, Scene scene) { 48 | 49 | resize(sizeX, sizeY); 50 | this.scene = scene; 51 | 52 | } 53 | 54 | public void resize(int sizeX, int sizeY, ArrayList values) { 55 | 56 | clear(); 57 | 58 | openCells.clear(); 59 | 60 | for (int i = 0; i < sizeY; i++) { 61 | 62 | add(new ArrayList()); 63 | 64 | for (int j = 0; j < sizeX; j++) { 65 | 66 | Cell c = new Cell(j, i, this); 67 | 68 | c.setValue(Random.choice(values)); 69 | 70 | get(i).add(c); 71 | 72 | openCells.add(c); 73 | 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | public void resize(int sizeX, int sizeY){ 81 | 82 | ArrayList s = new ArrayList(); 83 | s.add(EMPTY_CELL); 84 | resize(sizeX, sizeY, s); 85 | this.sizeX = sizeX; 86 | this.sizeY = sizeY; 87 | 88 | } 89 | 90 | public Cell get(Vector2f pos) { 91 | 92 | int fy = (int) getCoords(pos).y; 93 | int fx = (int) getCoords(pos).x; 94 | 95 | return get(fy).get(fx); 96 | 97 | } 98 | 99 | public Cell get(int x, int y) { 100 | 101 | return get(new Vector2f(x, y)); 102 | 103 | } 104 | 105 | public Vector2f getCoords(int posX, int posY){ 106 | 107 | Vector2f p = new Vector2f(posX, posY); 108 | 109 | p = enforceWrapBehavior(p); 110 | 111 | return p; 112 | 113 | } 114 | 115 | public Vector2f getCoords(Vector2f pos) { 116 | return getCoords(Math.round(pos.x), Math.round(pos.y)); 117 | } 118 | 119 | public void set(int x, int y, String value) { 120 | 121 | int fx = (int) getCoords(x, y).x; 122 | int fy = (int) getCoords(x, y).y; 123 | 124 | get(fy).get(fx).setValue(value); 125 | 126 | } 127 | 128 | public void set(Vector2f pos, String value) { 129 | set(Math.round(pos.x), Math.round(pos.y), value); 130 | } 131 | 132 | public Vector2f getMiddleCoords(){ 133 | 134 | return new Vector2f((int) size() / 2, (int) get(0).size() / 2); 135 | 136 | } 137 | 138 | public void setLine(Vector2f start, Vector2f end, String value) { 139 | 140 | Vector2f pos = new Vector2f(start); 141 | 142 | Vector2f nextPos = new Vector2f(pos); 143 | 144 | boolean axisX = false; 145 | 146 | Vector2f dir = end.minus(start); 147 | dir.normalize(); 148 | 149 | int rPX = Math.round(pos.x); 150 | int rPY = Math.round(pos.y); 151 | 152 | while (rPX != Math.round(end.x) || rPY != Math.round(end.y)) { 153 | 154 | if (axisX) 155 | nextPos.x += dir.x; 156 | else 157 | nextPos.y += dir.y; 158 | 159 | axisX = !axisX; 160 | 161 | rPX = Math.round(nextPos.x); 162 | rPY = Math.round(nextPos.y); 163 | 164 | //glow(pos, value); 165 | //glow(nextPos, value); 166 | 167 | get(pos).connect(get(nextPos), value); 168 | 169 | pos.x = nextPos.x; 170 | pos.y = nextPos.y; 171 | 172 | } 173 | 174 | } 175 | 176 | public void generateNodesAlgo(String[] nodeValues, String[] hallValues, ConnectionMode connectionMode, boolean connectOnce) { 177 | 178 | final ArrayList nodeVals = new ArrayList(); 179 | for (String i : nodeValues) 180 | nodeVals.add(i); 181 | 182 | final ArrayList hallVals = new ArrayList(); 183 | for (String i : hallValues) 184 | hallVals.add(i); 185 | 186 | if (seed != 0) 187 | Random.seed(seed); 188 | 189 | int nodeCount = 6; 190 | 191 | ArrayList nodes = new ArrayList(); 192 | 193 | for (int n = 0; n < nodeCount; n++) { 194 | nodes.add(new Cell(getRandomCoords(), this)); 195 | } 196 | 197 | if (connectionMode == ConnectionMode.HUB) { 198 | 199 | Cell hub = Random.choice(nodes); 200 | 201 | for (Cell node : nodes) { 202 | 203 | node.connect(hub, Random.choice(hallVals)); 204 | 205 | } 206 | 207 | } 208 | 209 | else if (connectionMode == ConnectionMode.ALL) { 210 | 211 | for (Cell node : nodes) { 212 | 213 | for (Cell other : nodes) { 214 | 215 | node.connect(other, Random.choice(hallVals)); 216 | 217 | } 218 | 219 | } 220 | 221 | } 222 | 223 | else if (connectionMode == ConnectionMode.ONE) { 224 | 225 | ArrayList unconnectedNodes = new ArrayList(nodes); 226 | 227 | for (Cell node : nodes) { 228 | 229 | if (unconnectedNodes.size() > 0) { 230 | 231 | Cell other = Random.choice(unconnectedNodes); 232 | 233 | if (unconnectedNodes.size() > 1 && other == node) { 234 | 235 | while(other == node) { 236 | other = Random.choice(unconnectedNodes); 237 | } 238 | 239 | } 240 | 241 | node.connect(other, Random.choice(hallVals)); 242 | 243 | if (other != node) 244 | unconnectedNodes.remove(node); 245 | 246 | } 247 | 248 | } 249 | 250 | } 251 | 252 | for (Cell n : nodes) { 253 | set(n.position(), Random.choice(nodeVals)); // Setting the lines messes this up 254 | } 255 | 256 | } 257 | 258 | public void generateMazeAlgo(int cellNum, int turnLeftWeight, int turnRightWeight, int straightWeight, String... genValues){ 259 | 260 | if (genValues.length == 0) 261 | { 262 | System.out.println("ERROR - generateMazeAlgo needs a list of values to choose for generation"); 263 | return; 264 | } 265 | 266 | if (seed != 0) 267 | Random.seed(seed); 268 | 269 | Vector2f pos = getRandomCoords(); 270 | 271 | Vector2f direction = new Vector2f(0, -1); 272 | 273 | int cN = Math.min(cellNum, size() * get(0).size() - 1); 274 | 275 | for (int c = 0; c < cN; c++) { 276 | 277 | ArrayList possibles = new ArrayList(); 278 | 279 | Vector2f l = BDXHMath.rotL(direction); 280 | Vector2f r = BDXHMath.rotR(direction); 281 | 282 | if (spaceEmpty(pos.plus(l))) { 283 | for (int i = 0; i < turnLeftWeight; i++) 284 | possibles.add(l); 285 | } 286 | if (spaceEmpty(pos.plus(r))) { 287 | for (int i = 0; i < turnRightWeight; i++) 288 | possibles.add(r); 289 | } 290 | if (spaceEmpty(pos.plus(direction))) { 291 | for (int i = 0; i < straightWeight; i++) 292 | possibles.add(direction); 293 | } 294 | 295 | if (possibles.size() > 0) { 296 | 297 | direction = Random.choice(possibles); 298 | 299 | Vector2f newPos = enforceWrapBehavior(pos.plus(direction)); 300 | 301 | get(pos).connect(get(newPos), Random.choice(genValues)); 302 | 303 | pos = newPos; 304 | 305 | } 306 | else { 307 | 308 | Cell nextOne = getRandomCell(Random.choice(genValues)); 309 | 310 | while(nextOne.equals(get(pos))) 311 | nextOne = getRandomCell(Random.choice(genValues)); 312 | 313 | pos = new Vector2f(nextOne.position()); 314 | 315 | c--; // This cell didn't work, basically, so decrease by one // This is a way to an infinite loop, so bekarful 316 | 317 | } 318 | 319 | } 320 | 321 | } 322 | 323 | public Vector2f enforceWrapBehavior(Vector2f pos){ 324 | if (wrapBehavior == Wrap.WRAP) { 325 | while (pos.y >= size()) 326 | pos.y -= size(); 327 | while (pos.y < 0) 328 | pos.y += size(); 329 | while (pos.x >= get(0).size()) 330 | pos.x -= size(); 331 | while (pos.x < 0) 332 | pos.x += size(); 333 | } 334 | else if (wrapBehavior == Wrap.CLAMP) { 335 | pos.y = Math.min(Math.max(pos.y, 0), size() - 1); 336 | pos.x = Math.min(Math.max(pos.x, 0), get(0).size() - 1); 337 | } 338 | return pos; 339 | } 340 | 341 | public void generateMazeAlgo(){ 342 | generateMazeAlgo((sizeX * sizeY) / 2, 1, 1, 1, "x"); 343 | } 344 | 345 | public boolean spaceEmpty(Vector2f pos) { 346 | 347 | Vector2f p = getCoords(pos); 348 | 349 | return get(p).getValue().equals(EMPTY_CELL); 350 | 351 | } 352 | 353 | public Vector2f getRandomCoords(){ 354 | 355 | return new Vector2f( (int) Random.random(0, size()), (int) Random.random(0, get(0).size())); 356 | 357 | } 358 | 359 | public Cell getRandomCell(String value) { 360 | 361 | Cell c = null; 362 | 363 | while (c == null) { 364 | 365 | Vector2f r = getRandomCoords(); 366 | 367 | Cell p = get(r); 368 | 369 | if (p.getValue() == value || value == null) 370 | c = p; 371 | 372 | } 373 | 374 | return c; 375 | 376 | } 377 | 378 | public String toString(){ 379 | 380 | String out = ""; 381 | 382 | for (ArrayList row : this) { 383 | out += row.toString(); 384 | out += "\n"; 385 | } 386 | 387 | return out; 388 | } 389 | 390 | public void placeObjects(String[] ends, String[] straights, String[] corners, 391 | String[] tEnds, String[] fourWays, String[] empties, Vector3f spawnPosition){ 392 | 393 | ArrayList endsAL = new ArrayList(Arrays.asList(ends)); 394 | ArrayList straightsAL = new ArrayList(Arrays.asList(straights)); 395 | ArrayList cornersAL = new ArrayList(Arrays.asList(corners)); 396 | ArrayList tEndsAL = new ArrayList(Arrays.asList(tEnds)); 397 | ArrayList fourWaysAL = new ArrayList(Arrays.asList(fourWays)); 398 | ArrayList emptiesAL = new ArrayList(Arrays.asList(empties)); 399 | 400 | for (int y = 0; y < size(); y++) { 401 | 402 | for (int x = 0; x < get(y).size(); x++) { 403 | 404 | Cell cell = get(x,y); 405 | 406 | GameObject createdCell = null; 407 | 408 | HashMap neighbors = cell.getNeighbors(); 409 | 410 | float ninety = (float) Math.PI / 2; 411 | 412 | if (cell.connections.size() == 0) { 413 | 414 | String emptyName = Random.choice(emptiesAL); 415 | 416 | if (emptyName != null) { 417 | 418 | createdCell = scene.add(Random.choice(emptiesAL)); 419 | 420 | ArrayList rots = new ArrayList(); 421 | 422 | rots.add(new Vector3f(0, 0, 0)); 423 | rots.add(new Vector3f(0, 0, (float) Math.PI / 2)); 424 | rots.add(new Vector3f(0, 0, (float) Math.PI)); 425 | rots.add(new Vector3f(0, 0, -(float) Math.PI / 2)); 426 | 427 | //createdCell.rotate(Random.dialogChoice(rots)); 428 | 429 | } 430 | 431 | } 432 | 433 | else if (cell.connections.size() == 1) { 434 | createdCell = scene.add(Random.choice(endsAL)); 435 | 436 | Vector3f rot = new Vector3f(cell.connections.get(0).position().x - cell.position().x, 437 | cell.position().y - cell.connections.get(0).position().y, 0); 438 | 439 | createdCell.alignAxisToVec(1, rot); 440 | createdCell.alignAxisToVec(2, new Vector3f(0, 0, 1)); 441 | 442 | } 443 | 444 | else if (cell.connections.size() == 2) { 445 | if (neighbors.containsKey("left") && neighbors.containsKey("right")) { 446 | createdCell = scene.add(Random.choice(straightsAL)); 447 | createdCell.rotate(0, 0, ninety); 448 | } 449 | else if (neighbors.containsKey("up") && neighbors.containsKey("down")) { 450 | createdCell = scene.add(Random.choice(straightsAL)); 451 | } 452 | else { 453 | 454 | createdCell = scene.add(Random.choice(cornersAL)); 455 | 456 | if (neighbors.containsKey("right") && neighbors.containsKey("down")) 457 | ; 458 | 459 | if (neighbors.containsKey("left") && neighbors.containsKey("down")) 460 | createdCell.rotate(0, 0, -ninety); 461 | 462 | if (neighbors.containsKey("right") && neighbors.containsKey("up")) 463 | createdCell.rotate(0, 0, ninety); 464 | 465 | if (neighbors.containsKey("left") && neighbors.containsKey("up")) 466 | createdCell.rotate(0, 0, ninety*2); 467 | } 468 | } 469 | else if (cell.connections.size() == 3) { 470 | 471 | createdCell = scene.add(Random.choice(tEndsAL)); 472 | 473 | if (!neighbors.containsKey("up")) 474 | ; 475 | if (!neighbors.containsKey("left")) 476 | createdCell.rotate(0, 0, ninety); 477 | if (!neighbors.containsKey("down")) 478 | createdCell.rotate(0, 0, ninety*2); 479 | if (!neighbors.containsKey("right")) 480 | createdCell.rotate(0, 0, -ninety); 481 | 482 | } 483 | else if (cell.connections.size() == 4) { 484 | createdCell = scene.add(Random.choice(fourWaysAL)); 485 | } 486 | 487 | if (createdCell != null) { 488 | 489 | Vector3f pos; 490 | 491 | if (spawnPosition == null) 492 | pos = new Vector3f(); 493 | else 494 | pos = new Vector3f(spawnPosition); 495 | 496 | Vector3f blockSize = createdCell.dimensions(); 497 | 498 | pos.y += size() * blockSize.y / 2; 499 | pos.x -= get(0).size() * blockSize.x / 2; 500 | 501 | pos.y -= (y * blockSize.y); 502 | pos.x += (x * blockSize.x); 503 | 504 | createdCell.position(pos); 505 | 506 | cell.createdRoom = createdCell; 507 | 508 | } 509 | 510 | } 511 | 512 | } 513 | 514 | } 515 | 516 | public void placeObjects(String end, String straight, String corner, String tEnd, String fourWays, String empty, Vector3f spawnPosition){ 517 | 518 | placeObjects(new String[]{end}, new String[]{straight}, new String[]{corner}, 519 | new String[]{tEnd}, new String[]{fourWays}, new String[]{empty}, spawnPosition); 520 | 521 | } 522 | 523 | } 524 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/utils/API.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.utils; 2 | 3 | import java.util.ArrayList; 4 | 5 | import javax.vecmath.Vector4f; 6 | 7 | import com.nilunder.bdx.GameObject; 8 | import com.nilunder.bdx.utils.Random; 9 | 10 | /** 11 | * Created by SolarLune on 1/15/2015. 12 | */ 13 | public final class API { 14 | 15 | static public boolean atLeastBooleans(int reqNum, boolean... checks){ 16 | 17 | int numGood = 0; 18 | 19 | for (boolean b : checks){ 20 | 21 | if (b) 22 | numGood += 1; 23 | 24 | } 25 | 26 | return numGood >= reqNum; 27 | 28 | } 29 | 30 | static public boolean atLeastNotBooleans(int reqNum, boolean... checks){ 31 | 32 | int numGood = 0; 33 | 34 | for (boolean b : checks){ 35 | 36 | if (!b) 37 | numGood += 1; 38 | 39 | } 40 | 41 | return numGood >= reqNum; 42 | 43 | } 44 | 45 | static public ArrayList sortByDistance(GameObject startingObject, ArrayList objectList) { 46 | 47 | ArrayList sorted = new ArrayList(); 48 | 49 | GameObject closest = objectList.get(0); 50 | 51 | sorted.add(closest); 52 | 53 | for (GameObject o : objectList) { 54 | 55 | if (startingObject.position().minus(o.position()).length() < startingObject.position().minus(closest.position()).length()) { 56 | 57 | closest = o; 58 | 59 | sorted.add(0, closest); 60 | 61 | } 62 | 63 | else 64 | 65 | sorted.add(closest); 66 | 67 | } 68 | 69 | return sorted; 70 | 71 | } 72 | 73 | static public Vector4f randomizeVector4f(Vector4f bottom, Vector4f top){ 74 | 75 | Vector4f vect = new Vector4f(1,1,1,1); 76 | 77 | vect.x = Random.random(bottom.x, top.x); 78 | vect.y = Random.random(bottom.y, top.y); 79 | vect.z = Random.random(bottom.z, top.z); 80 | vect.w = Random.random(bottom.w, top.w); 81 | 82 | return vect; 83 | 84 | } 85 | 86 | static public Vector4f randomizeVector4f(Vector4f top) { 87 | return randomizeVector4f(new Vector4f(0,0,0,0), top); 88 | } 89 | 90 | static public ArrayList getObjectByName(ArrayList collection, String name){ 91 | 92 | ArrayList list = new ArrayList(); 93 | 94 | for (GameObject g : collection) { 95 | 96 | if (g.name.contains(name)) 97 | list.add(g); 98 | 99 | } 100 | 101 | return list; 102 | 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/utils/BDXHMath.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.utils; 2 | 3 | import java.util.ArrayList; 4 | 5 | import javax.vecmath.Vector2f; 6 | import javax.vecmath.Vector3f; 7 | import javax.vecmath.Vector4f; 8 | 9 | import com.nilunder.bdx.Bdx; 10 | import com.nilunder.bdx.utils.Random; 11 | 12 | /** 13 | * Created by SolarLune on 1/9/2015. 14 | */ 15 | public final class BDXHMath { 16 | 17 | public static Vector3f snapVectToGrid(Vector3f vect, float gridSpacesPerUnit){ 18 | 19 | vect.x = Math.round(vect.x * gridSpacesPerUnit) / gridSpacesPerUnit; 20 | vect.y = Math.round(vect.y * gridSpacesPerUnit) / gridSpacesPerUnit; 21 | vect.z = Math.round(vect.z * gridSpacesPerUnit) / gridSpacesPerUnit; 22 | 23 | return vect; 24 | } 25 | 26 | public static Vector2f rotR(Vector2f in){ 27 | 28 | Vector2f v = new Vector2f(in); 29 | 30 | float temp = v.x; 31 | v.x = v.y; 32 | v.y = -temp; 33 | 34 | return v; 35 | 36 | } 37 | 38 | public static Vector2f rotL(Vector2f in){ 39 | 40 | Vector2f v = new Vector2f(in); 41 | 42 | float temp = v.x; 43 | v.x = -v.y; 44 | v.y = temp; 45 | 46 | return v; 47 | 48 | } 49 | 50 | public static Vector2f randomCardinalVector2f(){ 51 | 52 | ArrayList d = new ArrayList(); 53 | 54 | d.add(new Vector2f(1, 0)); 55 | d.add(new Vector2f(-1, 0)); 56 | d.add(new Vector2f(0, 1)); 57 | d.add(new Vector2f(0, -1)); 58 | 59 | return Random.choice(d); 60 | 61 | } 62 | 63 | public static float clamp(float value, float min, float max){ 64 | return Math.min(Math.max(value, min), max); 65 | } 66 | 67 | public static float clamp(float value, float maximums) { 68 | return clamp(value, -maximums, maximums); 69 | } 70 | 71 | public static Vector2f roundVector(Vector2f vec){ 72 | Vector2f out = new Vector2f(Math.round(vec.x), 73 | Math.round(vec.y)); 74 | return out; 75 | } 76 | 77 | public static Vector3f roundVector(Vector3f vec){ 78 | Vector3f out = new Vector3f(Math.round(vec.x), 79 | Math.round(vec.y), 80 | Math.round(vec.z)); 81 | return out; 82 | } 83 | 84 | public static Vector4f roundVector(Vector4f vec){ 85 | Vector4f out = new Vector4f(Math.round(vec.x), 86 | Math.round(vec.y), 87 | Math.round(vec.z), 88 | Math.round(vec.w)); 89 | return out; 90 | } 91 | 92 | public static float oscillateSin(float oscRate, float oscRange, boolean baseOffset, float timeOffset){ 93 | 94 | float value = (float) Math.sin((Bdx.time + timeOffset) * (Math.PI * 2) * oscRate) * oscRange; 95 | 96 | if (baseOffset) { 97 | value /= 2.0f; 98 | value += oscRange / 2; 99 | } 100 | 101 | return value; 102 | 103 | } 104 | 105 | public static float oscillateSin(float oscRate, float oscRange, boolean baseOffset){ 106 | return oscillateSin(oscRate, oscRange, baseOffset, 0); 107 | } 108 | 109 | public static float lerp(float valueOne, float valueTwo, float percent){ 110 | return valueOne + percent * (valueTwo - valueOne); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /BDX/bdxhelper/src/com/solarlune/bdxhelper/utils/Notifications.java: -------------------------------------------------------------------------------- 1 | package com.solarlune.bdxhelper.utils; 2 | 3 | import com.nilunder.bdx.Bdx; 4 | import com.nilunder.bdx.GameObject; 5 | import com.nilunder.bdx.utils.Named; 6 | 7 | import javax.vecmath.Vector3f; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | 11 | public class Notifications { 12 | 13 | static public class Note implements Named { 14 | 15 | public String name; 16 | public float timestamp; 17 | public Object data; 18 | 19 | public Note (String name) { 20 | this.name = name; 21 | timestamp = Bdx.time; 22 | } 23 | public Note (String name, Object data) { 24 | this(name); 25 | this.data = data; 26 | } 27 | 28 | public String toString(){ 29 | return name; 30 | } 31 | 32 | public String name(){ 33 | return name; 34 | } 35 | 36 | } 37 | 38 | static public class Notifier { 39 | public void onNote(Note note){}; 40 | } 41 | 42 | public HashMap> registered; 43 | 44 | public Notifications(){ 45 | registered = new HashMap>(); 46 | } 47 | 48 | public void broadcast(Note note, String groupName){ 49 | if (registered.containsKey(groupName)) { 50 | for (Notifier n : new ArrayList(registered.get(groupName))) { 51 | n.onNote(note); 52 | } 53 | } 54 | } 55 | 56 | public void broadcast(Note note) { 57 | for (String groupName : registered.keySet()) { 58 | broadcast(note, groupName); 59 | } 60 | } 61 | 62 | public void broadcast(String noteName) { 63 | broadcast(new Note(noteName)); 64 | } 65 | 66 | public void register(Notifier n, String groupName) { 67 | if (!registered.containsKey(groupName)) 68 | registered.put(groupName, new ArrayList()); 69 | registered.get(groupName).add(n); 70 | } 71 | 72 | public void register(Notifier n) { 73 | register(n, "default"); 74 | } 75 | 76 | public void unregister(Notifier n, String groupName) { 77 | if (!registered.containsKey(groupName)) 78 | registered.put(groupName, new ArrayList()); 79 | if (registered.get(groupName) != null) 80 | registered.get(groupName).remove(n); 81 | } 82 | 83 | public void unregister(Notifier n){ 84 | unregister(n, "default"); 85 | } 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /BGE/API Stub/aud.py: -------------------------------------------------------------------------------- 1 | '''This module provides access to the audaspace audio library.''' 2 | 3 | AUD_DEVICE_JACK = int 4 | AUD_DEVICE_NULL = int 5 | AUD_DEVICE_OPENAL = int 6 | AUD_DEVICE_SDL = int 7 | AUD_DISTANCE_MODEL_EXPONENT = int 8 | AUD_DISTANCE_MODEL_EXPONENT_CLAMPED = int 9 | AUD_DISTANCE_MODEL_INVALID = int 10 | AUD_DISTANCE_MODEL_INVERSE = int 11 | AUD_DISTANCE_MODEL_INVERSE_CLAMPED = int 12 | AUD_DISTANCE_MODEL_LINEAR = int 13 | AUD_DISTANCE_MODEL_LINEAR_CLAMPED = int 14 | AUD_FORMAT_FLOAT32 = int 15 | AUD_FORMAT_FLOAT64 = int 16 | AUD_FORMAT_INVALID = int 17 | AUD_FORMAT_S16 = int 18 | AUD_FORMAT_S24 = int 19 | AUD_FORMAT_S32 = int 20 | AUD_FORMAT_U8 = int 21 | AUD_STATUS_INVALID = int 22 | AUD_STATUS_PAUSED = int 23 | AUD_STATUS_PLAYING = int 24 | AUD_STATUS_STOPPED = int 25 | class Device: 26 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 27 | 28 | channels = getset_descriptor 29 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 30 | 31 | distance_model = getset_descriptor 32 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 33 | 34 | doppler_factor = getset_descriptor 35 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 36 | 37 | format = getset_descriptor 38 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 39 | 40 | listener_location = getset_descriptor 41 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 42 | 43 | listener_orientation = getset_descriptor 44 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 45 | 46 | listener_velocity = getset_descriptor 47 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 48 | 49 | def lock(*argv): 50 | '''lock() 51 | 52 | Locks the device so that it's guaranteed, that no samples are read from the streams until :meth:`unlock` is called. 53 | This is useful if you want to do start/stop/pause/resume some sounds at the same time. 54 | 55 | .. note:: The device has to be unlocked as often as locked to be able to continue playback. 56 | 57 | .. warning:: Make sure the time between locking and unlocking is as short as possible to avoid clicks.''' 58 | 59 | def play(*argv): 60 | '''play(factory, keep=False) 61 | 62 | Plays a factory. 63 | 64 | :arg factory: The factory to play. 65 | :type factory: :class:`Factory` 66 | :arg keep: See :attr:`Handle.keep`. 67 | :type keep: bool 68 | :return: The playback handle with which playback can be controlled with. 69 | :rtype: :class:`Handle`''' 70 | 71 | rate = getset_descriptor 72 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 73 | 74 | speed_of_sound = getset_descriptor 75 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 76 | 77 | def stopAll(*argv): 78 | '''stopAll() 79 | 80 | Stops all playing and paused sounds.''' 81 | 82 | def unlock(*argv): 83 | '''unlock() 84 | 85 | Unlocks the device after a lock call, see :meth:`lock` for details.''' 86 | 87 | volume = getset_descriptor 88 | '''Device objects represent an audio output backend like OpenAL or SDL, but might also represent a file output or RAM buffer output.''' 89 | 90 | 91 | class Factory: 92 | '''Factory objects are immutable and represent a sound that can be played simultaneously multiple times. They are called factories because they create reader objects internally that are used for playback.''' 93 | 94 | def buffer(*argv): 95 | '''buffer() 96 | 97 | Buffers a factory into RAM. 98 | This saves CPU usage needed for decoding and file access if the underlying factory reads from a file on the harddisk, but it consumes a lot of memory. 99 | 100 | :return: The created :class:`Factory` object. 101 | :rtype: :class:`Factory` 102 | 103 | .. note:: Only known-length factories can be buffered. 104 | 105 | .. warning:: Raw PCM data needs a lot of space, only buffer short factories.''' 106 | 107 | def delay(*argv): 108 | '''delay(time) 109 | 110 | Delays by playing adding silence in front of the other factory's data. 111 | 112 | :arg time: How many seconds of silence should be added before the factory. 113 | :type time: float 114 | :return: The created :class:`Factory` object. 115 | :rtype: :class:`Factory`''' 116 | 117 | def fadein(*argv): 118 | '''fadein(start, length) 119 | 120 | Fades a factory in by raising the volume linearly in the given time interval. 121 | 122 | :arg start: Time in seconds when the fading should start. 123 | :type start: float 124 | :arg length: Time in seconds how long the fading should last. 125 | :type length: float 126 | :return: The created :class:`Factory` object. 127 | :rtype: :class:`Factory` 128 | 129 | .. note:: Before the fade starts it plays silence.''' 130 | 131 | def fadeout(*argv): 132 | '''fadeout(start, length) 133 | 134 | Fades a factory in by lowering the volume linearly in the given time interval. 135 | 136 | :arg start: Time in seconds when the fading should start. 137 | :type start: float 138 | :arg length: Time in seconds how long the fading should last. 139 | :type length: float 140 | :return: The created :class:`Factory` object. 141 | :rtype: :class:`Factory` 142 | 143 | .. note:: After the fade this factory plays silence, so that the length of the factory is not altered.''' 144 | 145 | def file(*argv): 146 | '''file(filename) 147 | 148 | Creates a factory object of a sound file. 149 | 150 | :arg filename: Path of the file. 151 | :type filename: string 152 | :return: The created :class:`Factory` object. 153 | :rtype: :class:`Factory` 154 | 155 | .. warning:: If the file doesn't exist or can't be read you will not get an exception immediately, but when you try to start playback of that factory.''' 156 | 157 | def filter(*argv): 158 | '''filter(b, a = (1)) 159 | 160 | Filters a factory with the supplied IIR filter coefficients. 161 | Without the second parameter you'll get a FIR filter. 162 | If the first value of the a sequence is 0 it will be set to 1 automatically. 163 | If the first value of the a sequence is neither 0 nor 1, all filter coefficients will be scaled by this value so that it is 1 in the end, you don't have to scale yourself. 164 | 165 | :arg b: The nominator filter coefficients. 166 | :type b: sequence of float 167 | :arg a: The denominator filter coefficients. 168 | :type a: sequence of float 169 | :return: The created :class:`Factory` object. 170 | :rtype: :class:`Factory`''' 171 | 172 | def highpass(*argv): 173 | '''highpass(frequency, Q=0.5) 174 | 175 | Creates a second order highpass filter based on the transfer function H(s) = s^2 / (s^2 + s/Q + 1) 176 | 177 | :arg frequency: The cut off trequency of the highpass. 178 | :type frequency: float 179 | :arg Q: Q factor of the lowpass. 180 | :type Q: float 181 | :return: The created :class:`Factory` object. 182 | :rtype: :class:`Factory`''' 183 | 184 | def join(*argv): 185 | '''join(factory) 186 | 187 | Plays two factories in sequence. 188 | 189 | :arg factory: The factory to play second. 190 | :type factory: :class:`Factory` 191 | :return: The created :class:`Factory` object. 192 | :rtype: :class:`Factory` 193 | 194 | .. note:: The two factories have to have the same specifications (channels and samplerate).''' 195 | 196 | def limit(*argv): 197 | '''limit(start, end) 198 | 199 | Limits a factory within a specific start and end time. 200 | 201 | :arg start: Start time in seconds. 202 | :type start: float 203 | :arg end: End time in seconds. 204 | :type end: float 205 | :return: The created :class:`Factory` object. 206 | :rtype: :class:`Factory`''' 207 | 208 | def loop(*argv): 209 | '''loop(count) 210 | 211 | Loops a factory. 212 | 213 | :arg count: How often the factory should be looped. Negative values mean endlessly. 214 | :type count: integer 215 | :return: The created :class:`Factory` object. 216 | :rtype: :class:`Factory` 217 | 218 | .. note:: This is a filter function, you might consider using :attr:`Handle.loop_count` instead.''' 219 | 220 | def lowpass(*argv): 221 | '''lowpass(frequency, Q=0.5) 222 | 223 | Creates a second order lowpass filter based on the transfer function H(s) = 1 / (s^2 + s/Q + 1) 224 | 225 | :arg frequency: The cut off trequency of the lowpass. 226 | :type frequency: float 227 | :arg Q: Q factor of the lowpass. 228 | :type Q: float 229 | :return: The created :class:`Factory` object. 230 | :rtype: :class:`Factory`''' 231 | 232 | def mix(*argv): 233 | '''mix(factory) 234 | 235 | Mixes two factories. 236 | 237 | :arg factory: The factory to mix over the other. 238 | :type factory: :class:`Factory` 239 | :return: The created :class:`Factory` object. 240 | :rtype: :class:`Factory` 241 | 242 | .. note:: The two factories have to have the same specifications (channels and samplerate).''' 243 | 244 | def pingpong(*argv): 245 | '''pingpong() 246 | 247 | Plays a factory forward and then backward. 248 | This is like joining a factory with its reverse. 249 | 250 | :return: The created :class:`Factory` object. 251 | :rtype: :class:`Factory`''' 252 | 253 | def pitch(*argv): 254 | '''pitch(factor) 255 | 256 | Changes the pitch of a factory with a specific factor. 257 | 258 | :arg factor: The factor to change the pitch with. 259 | :type factor: float 260 | :return: The created :class:`Factory` object. 261 | :rtype: :class:`Factory` 262 | 263 | .. note:: This is done by changing the sample rate of the underlying factory, which has to be an integer, so the factor value rounded and the factor may not be 100 % accurate. 264 | 265 | .. note:: This is a filter function, you might consider using :attr:`Handle.pitch` instead.''' 266 | 267 | def reverse(*argv): 268 | '''reverse() 269 | 270 | Plays a factory reversed. 271 | 272 | :return: The created :class:`Factory` object. 273 | :rtype: :class:`Factory` 274 | 275 | .. note:: The factory has to have a finite length and has to be seekable. It's recommended to use this only with factories with fast and accurate seeking, which is not true for encoded audio files, such ones should be buffered using :meth:`buffer` before being played reversed. 276 | 277 | .. warning:: If seeking is not accurate in the underlying factory you'll likely hear skips/jumps/cracks.''' 278 | 279 | def sine(*argv): 280 | '''sine(frequency, rate=44100) 281 | 282 | Creates a sine factory which plays a sine wave. 283 | 284 | :arg frequency: The frequency of the sine wave in Hz. 285 | :type frequency: float 286 | :arg rate: The sampling rate in Hz. It's recommended to set this value to the playback device's samling rate to avoid resamping. 287 | :type rate: int 288 | :return: The created :class:`Factory` object. 289 | :rtype: :class:`Factory`''' 290 | 291 | def square(*argv): 292 | '''square(threshold = 0) 293 | 294 | Makes a square wave out of an audio wave by setting all samples with a amplitude >= threshold to 1, all <= -threshold to -1 and all between to 0. 295 | 296 | :arg threshold: Threshold value over which an amplitude counts non-zero. 297 | :type threshold: float 298 | :return: The created :class:`Factory` object. 299 | :rtype: :class:`Factory`''' 300 | 301 | def volume(*argv): 302 | '''volume(volume) 303 | 304 | Changes the volume of a factory. 305 | 306 | :arg volume: The new volume.. 307 | :type volume: float 308 | :return: The created :class:`Factory` object. 309 | :rtype: :class:`Factory` 310 | 311 | .. note:: Should be in the range [0, 1] to avoid clipping. 312 | 313 | .. note:: This is a filter function, you might consider using :attr:`Handle.volume` instead.''' 314 | 315 | 316 | class Handle: 317 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 318 | 319 | attenuation = getset_descriptor 320 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 321 | 322 | cone_angle_inner = getset_descriptor 323 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 324 | 325 | cone_angle_outer = getset_descriptor 326 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 327 | 328 | cone_volume_outer = getset_descriptor 329 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 330 | 331 | distance_maximum = getset_descriptor 332 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 333 | 334 | distance_reference = getset_descriptor 335 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 336 | 337 | keep = getset_descriptor 338 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 339 | 340 | location = getset_descriptor 341 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 342 | 343 | loop_count = getset_descriptor 344 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 345 | 346 | orientation = getset_descriptor 347 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 348 | 349 | def pause(*argv): 350 | '''pause() 351 | 352 | Pauses playback. 353 | 354 | :return: Whether the action succeeded. 355 | :rtype: bool''' 356 | 357 | pitch = getset_descriptor 358 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 359 | 360 | position = getset_descriptor 361 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 362 | 363 | relative = getset_descriptor 364 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 365 | 366 | def resume(*argv): 367 | '''resume() 368 | 369 | Resumes playback. 370 | 371 | :return: Whether the action succeeded. 372 | :rtype: bool''' 373 | 374 | status = getset_descriptor 375 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 376 | 377 | def stop(*argv): 378 | '''stop() 379 | 380 | Stops playback. 381 | 382 | :return: Whether the action succeeded. 383 | :rtype: bool 384 | 385 | .. note:: This makes the handle invalid.''' 386 | 387 | velocity = getset_descriptor 388 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 389 | 390 | volume = getset_descriptor 391 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 392 | 393 | volume_maximum = getset_descriptor 394 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 395 | 396 | volume_minimum = getset_descriptor 397 | '''Handle objects are playback handles that can be used to control playback of a sound. If a sound is played back multiple times then there are as many handles.''' 398 | 399 | 400 | def device(*argv): 401 | '''device() 402 | 403 | Returns the application's :class:`Device`. 404 | 405 | :return: The application's :class:`Device`. 406 | :rtype: :class:`Device`''' 407 | 408 | class error: 409 | args = getset_descriptor 410 | def with_traceback(*argv): 411 | '''Exception.with_traceback(tb) -- 412 | set self.__traceback__ to tb and return self.''' 413 | 414 | 415 | 416 | -------------------------------------------------------------------------------- /BGE/API Stub/blf.py: -------------------------------------------------------------------------------- 1 | '''This module provides access to blenders text drawing functions.''' 2 | 3 | CLIPPING = int 4 | KERNING_DEFAULT = int 5 | ROTATION = int 6 | SHADOW = int 7 | def aspect(*argv): 8 | '''.. function:: aspect(fontid, aspect) 9 | 10 | Set the aspect for drawing text. 11 | 12 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 13 | :type fontid: int 14 | :arg aspect: The aspect ratio for text drawing to use. 15 | :type aspect: float''' 16 | 17 | def blur(*argv): 18 | '''.. function:: blur(fontid, radius) 19 | 20 | Set the blur radius for drawing text. 21 | 22 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 23 | :type fontid: int 24 | :arg radius: The radius for blurring text (in pixels). 25 | :type radius: int''' 26 | 27 | def clipping(*argv): 28 | '''.. function:: clipping(fontid, xmin, ymin, xmax, ymax) 29 | 30 | Set the clipping, enable/disable using CLIPPING. 31 | 32 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 33 | :type fontid: int 34 | :arg xmin: Clip the drawing area by these bounds. 35 | :type xmin: float 36 | :arg ymin: Clip the drawing area by these bounds. 37 | :type ymin: float 38 | :arg xmax: Clip the drawing area by these bounds. 39 | :type xmax: float 40 | :arg ymax: Clip the drawing area by these bounds. 41 | :type ymax: float''' 42 | 43 | def dimensions(*argv): 44 | '''.. function:: dimensions(fontid, text) 45 | 46 | Return the width and height of the text. 47 | 48 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 49 | :type fontid: int 50 | :arg text: the text to draw. 51 | :type text: string 52 | :return: the width and height of the text. 53 | :rtype: tuple of 2 floats''' 54 | 55 | def disable(*argv): 56 | '''.. function:: disable(fontid, option) 57 | 58 | Disable option. 59 | 60 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 61 | :type fontid: int 62 | :arg option: One of ROTATION, CLIPPING, SHADOW or KERNING_DEFAULT. 63 | :type option: int''' 64 | 65 | def draw(*argv): 66 | '''.. function:: draw(fontid, text) 67 | 68 | Draw text in the current context. 69 | 70 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 71 | :type fontid: int 72 | :arg text: the text to draw. 73 | :type text: string''' 74 | 75 | def enable(*argv): 76 | '''.. function:: enable(fontid, option) 77 | 78 | Enable option. 79 | 80 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 81 | :type fontid: int 82 | :arg option: One of ROTATION, CLIPPING, SHADOW or KERNING_DEFAULT. 83 | :type option: int''' 84 | 85 | def load(*argv): 86 | '''.. function:: load(filename) 87 | 88 | Load a new font. 89 | 90 | :arg filename: the filename of the font. 91 | :type filename: string 92 | :return: the new font's fontid or -1 if there was an error. 93 | :rtype: integer''' 94 | 95 | def position(*argv): 96 | '''.. function:: position(fontid, x, y, z) 97 | 98 | Set the position for drawing text. 99 | 100 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 101 | :type fontid: int 102 | :arg x: X axis position to draw the text. 103 | :type x: float 104 | :arg y: Y axis position to draw the text. 105 | :type y: float 106 | :arg z: Z axis position to draw the text. 107 | :type z: float''' 108 | 109 | def rotation(*argv): 110 | '''.. function:: rotation(fontid, angle) 111 | 112 | Set the text rotation angle, enable/disable using ROTATION. 113 | 114 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 115 | :type fontid: int 116 | :arg angle: The angle for text drawing to use. 117 | :type angle: float''' 118 | 119 | def shadow(*argv): 120 | '''.. function:: shadow(fontid, level, r, g, b, a) 121 | 122 | Shadow options, enable/disable using SHADOW . 123 | 124 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 125 | :type fontid: int 126 | :arg level: The blur level, can be 3, 5 or 0. 127 | :type level: int 128 | :arg r: Shadow color (red channel 0.0 - 1.0). 129 | :type r: float 130 | :arg g: Shadow color (green channel 0.0 - 1.0). 131 | :type g: float 132 | :arg b: Shadow color (blue channel 0.0 - 1.0). 133 | :type b: float 134 | :arg a: Shadow color (alpha channel 0.0 - 1.0). 135 | :type a: float''' 136 | 137 | def shadow_offset(*argv): 138 | '''.. function:: shadow_offset(fontid, x, y) 139 | 140 | Set the offset for shadow text. 141 | 142 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 143 | :type fontid: int 144 | :arg x: Vertical shadow offset value in pixels. 145 | :type x: float 146 | :arg y: Horizontal shadow offset value in pixels. 147 | :type y: float''' 148 | 149 | def size(*argv): 150 | '''.. function:: size(fontid, size, dpi) 151 | 152 | Set the size and dpi for drawing text. 153 | 154 | :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default font use 0. 155 | :type fontid: int 156 | :arg size: Point size of the font. 157 | :type size: int 158 | :arg dpi: dots per inch value to use for drawing. 159 | :type dpi: int''' 160 | 161 | def unload(*argv): 162 | '''.. function:: unload(filename) 163 | 164 | Unload an existing font. 165 | 166 | :arg filename: the filename of the font. 167 | :type filename: string''' 168 | 169 | 170 | -------------------------------------------------------------------------------- /BGE/bghelper/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2013-2014 SolarLune 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software, associated scripts (i.e. 5 | bghelper.api, bghelper.game, etc.), and associated documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 8 | following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 15 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | P.S. It would be nice if you could attribute me for the creation of this and my other scripts. Thanks! 18 | """ 19 | 20 | 21 | from . import api 22 | from . import audio 23 | from . import filters 24 | from . import game 25 | from . import input 26 | from . import light 27 | from . import math 28 | from . import mesh 29 | from . import nodemap 30 | from . import rlg 31 | from . import sprites 32 | from . import time 33 | from . import trail 34 | from . import viewport 35 | from . import window -------------------------------------------------------------------------------- /BGE/bghelper/api.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import sys 4 | 5 | from bge import logic 6 | 7 | 8 | class Scenes: 9 | def __str__(self): 10 | return str(logic.getSceneList()) 11 | 12 | def __len__(self): 13 | return len(logic.getSceneList()) 14 | 15 | def __iter__(self): 16 | return iter(logic.getSceneList()) 17 | 18 | def __contains__(self, key): 19 | if isinstance(key, str): 20 | return True if key in {s.name for s in logic.getSceneList()} else False 21 | raise TypeError('must be str, not ' + type(key).__name__) 22 | 23 | def __getitem__(self, key): 24 | scenes = logic.getSceneList() 25 | if isinstance(key, str): 26 | return {s.name: s for s in scenes}[key] 27 | return scenes[key] 28 | 29 | def set(self, key): 30 | logic.getCurrentScene().replace(key) 31 | 32 | def add(self, key, overlay=True): 33 | logic.addScene(key, overlay) 34 | 35 | def __del__(self): 36 | del self 37 | 38 | def remove(self, key): 39 | self.__getitem__(key).end() 40 | 41 | def leave(self, *args): 42 | 43 | """ 44 | Ends all scenes except the ones you specify. 45 | arguments = the names of the scenes you want to leave 46 | """ 47 | for scene in logic.getSceneList(): 48 | if not scene.name in args: 49 | scene.end() 50 | 51 | 52 | scenes = Scenes() 53 | 54 | 55 | class Callback(object): 56 | """ 57 | A callback class for making callback objects. These objects are useful for telling when a value changes - for example, you 58 | could use a callback to tell when the Player collides with an object other than the one he is currently colliding with (None, if 59 | he isn't colliding with anything). You can use a callback object to execute a function when the callback returns - in the above 60 | example, you could create an object or particle when this callback returns positive. 61 | """ 62 | 63 | def __init__(self): 64 | 65 | self.calls = dict({}) 66 | self.idindex = 0 67 | 68 | def add(self, check, onchange, cb_name=None, defaultvalue=1, mode=0): 69 | 70 | """ 71 | Add a callback to the list for this Callback object to take care of. 72 | 73 | check = Checking function for the callback (e.g. a function that returns obj.position.x < 10), like: 74 | 75 | lambda : obj.getDistanceTo(sce.objects['Dest']) < 5 76 | 77 | onchange = Returning function for the callback (e.g. a function that does: obj.endObject(); 78 | this would run when obj.position.x < 10, but only the one frame that this is true (default)) 79 | 80 | cb_name = Name / ID for the callback; Not necessary, but is used to refer to the callback later. Can be a number, string, etc. 81 | 82 | defaultvalue = What the default value is; when the callback is first added, if defaultvalue = 0, 83 | then the previous value recorded for the callback will be None. Unless the checking function returns None, then the returning function will run. 84 | 85 | mode = (0 by default); what mode the callback is; 0 == changed, 1 == equals, 86 | 2 == greater than, 3 == less than; try to keep this set to 0. 87 | 88 | This function also returns the original callback ID, so you can get it later if you need to. 89 | """ 90 | 91 | if defaultvalue == 0: 92 | callback = [check, onchange, mode, None] 93 | else: 94 | callback = [check, onchange, mode, check()] 95 | 96 | if cb_name is None: # If you don't specify an ID, then it will return an internal ID counter 97 | cb_name = self.idindex 98 | self.idindex += 1 99 | 100 | self.calls[cb_name] = callback # A list of all calls for this callback object - the checking function, 101 | # the function to return if the callback is true, the mode, as well as the previous value (used by the Change 102 | # mode, which is default) 103 | 104 | return cb_name # Returns the ID number if you want to keep track of it and access the Callback 105 | # object's callbacks through object.calls[id] 106 | 107 | def remove(self, cb_name=None): 108 | 109 | """ 110 | Removes the callback with the specified ID from the list of callback functions for this object. 111 | """ 112 | 113 | if cb_name is None: 114 | self.calls.popitem() 115 | else: 116 | del self.calls[cb_name] 117 | 118 | def remove_all(self): 119 | 120 | self.calls.clear() 121 | 122 | def get(self, cb_name): 123 | 124 | return self.calls[cb_name] 125 | 126 | def update(self): 127 | """ 128 | Update the callbacks, and run their associated functions if they change, or for whatever mode each callback is in. 129 | """ 130 | 131 | for c in self.calls: 132 | 133 | call = self.calls[c] 134 | 135 | value = call[0]() 136 | 137 | mode = call[2] 138 | 139 | if call[3] != value and mode == 0: # Changed (not equal) 140 | call[1](call[3]) 141 | call[3] = value 142 | elif call[3] == value and mode == 1: # Equals (experimental; stick with mode 0) 143 | call[1](call[3]) 144 | call[3] = value 145 | elif call[3] > value and mode == 2: # Greater than (experimental; stick with mode 0) 146 | call[1](call[3]) 147 | call[3] = value 148 | elif call[3] < value and mode == 3: # Less than (experimental; stick with mode 0) 149 | call[1](call[3]) 150 | call[3] = value 151 | 152 | 153 | callbacks = Callback() 154 | 155 | 156 | def end_game_on_exception(): 157 | """ 158 | Ends the game if an exception is raised. 159 | :return: None 160 | """ 161 | 162 | exc = sys.exc_info() 163 | 164 | if exc[0]: # An exception has been raised; end the game 165 | 166 | # if not allow_missing_functions and not "'module' object has no attribute" in str(sys.last_value): 167 | # It's an actual error, not just an object's function that hasn't been implemented 168 | 169 | logic.endGame() 170 | 171 | 172 | def mutate(intended_class, obj=None): 173 | 174 | """ 175 | Mutates the object into an instance of the intended class. 176 | :param intended_class: class - The kind of class you want the object to override and become one of. 177 | :param obj: KX_GameObject - The object to mutate. If not specified or left to None, 178 | then it will use the calling object. 179 | :return: intended_class 180 | """ 181 | 182 | if obj is None: 183 | 184 | obj = logic.getCurrentController().owner 185 | 186 | if isinstance(obj, intended_class): 187 | 188 | return obj 189 | 190 | else: 191 | 192 | return intended_class(obj) -------------------------------------------------------------------------------- /BGE/bghelper/audio.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import os 4 | import random 5 | 6 | import aud 7 | from bge import logic 8 | 9 | 10 | class AudioDevice(): 11 | 12 | BGM_STATUS_STOPPED = 0 13 | BGM_STATUS_FADING_IN = 1 14 | BGM_STATUS_PLAYING = 2 15 | BGM_STATUS_FADING_OUT = 3 16 | 17 | def __init__(self, sound_folder='//assets/snd/'): 18 | 19 | self.device = aud.device() 20 | 21 | self.sounds = {} 22 | 23 | self.set_audio_folder(sound_folder) 24 | 25 | self.bgm_list = [] 26 | 27 | self.sound_volume = 1.0 28 | self.bgm_volume = 1.0 29 | 30 | self.fade_time = 1.0 # Fade time between BGM plays 31 | 32 | def set_audio_folder(self, sound_folder): 33 | 34 | if not sound_folder.endswith("/"): 35 | sound_folder += "/" # Make sure there's a trailing slash for sound files to go 36 | 37 | sound_folder = logic.expandPath(sound_folder) 38 | 39 | self.sounds = {} 40 | 41 | for snd_info in os.walk(sound_folder): 42 | 43 | for f in snd_info[2]: 44 | 45 | filename = os.path.splitext(f)[0] # Ignore the extension; we're just 46 | # interested in the audio file itself 47 | 48 | self.sounds[filename] = aud.Factory(sound_folder + f) 49 | 50 | def update_bgm(self): 51 | 52 | fr = ((1.0 / logic.getLogicTicRate()) * self.bgm_volume ) / self.fade_time 53 | 54 | list = self.bgm_list[:] 55 | 56 | for b in range(len(list)): 57 | 58 | bgm = list[b] 59 | 60 | if bgm['status'] == self.BGM_STATUS_STOPPED: 61 | 62 | bgm['handle'].stop() 63 | self.bgm_list.remove(bgm) 64 | 65 | if bgm['status'] == self.BGM_STATUS_FADING_IN: 66 | 67 | bgm['volume'] += fr * self.bgm_volume 68 | 69 | if bgm['volume'] > self.bgm_volume: 70 | 71 | bgm['volume'] = self.bgm_volume 72 | bgm['status'] = self.BGM_STATUS_PLAYING 73 | 74 | elif bgm['status'] == self.BGM_STATUS_PLAYING: 75 | 76 | if b < len(self.bgm_list) - 1: 77 | 78 | bgm['status'] = self.BGM_STATUS_FADING_OUT # Begin fading out "extra" tracks 79 | 80 | elif bgm['status'] == self.BGM_STATUS_FADING_OUT: 81 | 82 | bgm['volume'] -= fr * self.bgm_volume 83 | 84 | if bgm['volume'] <= 0: 85 | 86 | self.bgm_list.remove(bgm) 87 | bgm['handle'].stop() 88 | 89 | if bgm['handle'].status == aud.AUD_STATUS_PLAYING: 90 | 91 | bgm['handle'].volume = bgm['volume'] 92 | 93 | def play_bgm(self, bgm): 94 | 95 | if self.bgm_volume > 0: 96 | 97 | if not self.bgm_list or self.bgm_list[-1]['name'] != bgm: 98 | 99 | h = self.device.play(self.sounds[bgm]) 100 | h.loop_count = -1 101 | h.volume = 0.0 102 | 103 | d = { # Create a new "BGM" entry. 104 | 105 | 'name': bgm, 106 | 'handle': h, 107 | 'volume': 0.0, 108 | 'status': self.BGM_STATUS_FADING_IN 109 | 110 | } 111 | 112 | self.bgm_list.append(d) 113 | 114 | def play_bgm_old(self, bgm, loop=-1): 115 | 116 | if self.bgm_volume > 0.0: 117 | 118 | if bgm != self.current_bgm_handle: 119 | 120 | self.current_bgm_handle = bgm 121 | self.current_bgm_handle = self.device.play(self.sounds[bgm]) 122 | self.current_bgm_handle.loop_count = loop 123 | self.current_bgm_handle.volume = self.bgm_volume 124 | return self.current_bgm_handle 125 | 126 | return None 127 | 128 | def stop_bgm(self): 129 | 130 | if self.bgm_list: 131 | 132 | self.bgm_list[0]['status'] = self.BGM_STATUS_STOPPED 133 | 134 | def stop_all_bgm(self): 135 | 136 | for b in self.bgm_list: 137 | 138 | b['status'] = self.BGM_STATUS_STOPPED 139 | 140 | def play_sound(self, sound, volume_var=0.0, pitch_var=.1): 141 | 142 | if self.sound_volume > 0.0: 143 | 144 | handle = self.device.play(self.sounds[sound]) 145 | handle.volume = self.sound_volume 146 | handle.volume += random.uniform(0.0, volume_var) 147 | handle.pitch += random.uniform(-pitch_var, pitch_var) 148 | return handle 149 | 150 | def get_bgm_handle(self): 151 | 152 | if self.bgm_list: 153 | return self.bgm_list[0]['handle'] 154 | else: 155 | return None 156 | 157 | 158 | device = AudioDevice() -------------------------------------------------------------------------------- /BGE/bghelper/example.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarLune/SolHelp/3c903d629685b56b12511fe38da5dca13d55f88a/BGE/bghelper/example.blend -------------------------------------------------------------------------------- /BGE/bghelper/game.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic 4 | 5 | import mathutils 6 | 7 | from . import window 8 | 9 | 10 | ML_LOCK_NONE = 0 # Constants for mouse-look axis locking 11 | ML_LOCK_X = 1 12 | ML_LOCK_Y = 2 13 | 14 | 15 | # TODO: Add axis setting to allow for joystick analog controlled mouse-look 16 | 17 | def mouse_look(obj=None, accel_speed=1.0, max_speed=45.0, friction=8.0, lock=ML_LOCK_NONE): 18 | 19 | if not obj: 20 | 21 | obj = logic.getCurrentController().owner 22 | 23 | if not 'mouse_look_info' in obj: 24 | 25 | obj['mouse_look_info'] = { 26 | 27 | 'rot_x': 0.0, 28 | 'speed': mathutils.Vector([0, 0]), 29 | } 30 | 31 | else: 32 | 33 | mouse_position = logic.mouse.position 34 | 35 | delta_x = 0.5 - mouse_position[0] 36 | delta_y = 0.5 - mouse_position[1] 37 | 38 | margin = 0.002 39 | 40 | if abs(delta_x) < margin: 41 | delta_x = 0.0 42 | if abs(delta_y) < margin: 43 | delta_y = 0.0 44 | 45 | divisor = .0025 46 | 47 | info = obj['mouse_look_info'] 48 | speed = info['speed'] 49 | 50 | if not lock & ML_LOCK_X: 51 | speed.x += delta_x * (accel_speed + friction * divisor) 52 | 53 | if not lock & ML_LOCK_Y: 54 | speed.y += (delta_y * (accel_speed + friction * divisor)) / window.get_aspect_ratio() 55 | 56 | speed.magnitude = min(speed.magnitude, max_speed * divisor) 57 | 58 | obj.applyRotation([0, 0, speed.x], 0) 59 | obj.applyRotation([speed.y, 0, 0], 1) 60 | 61 | if speed.magnitude > friction * divisor: 62 | speed.magnitude -= friction * divisor 63 | else: 64 | speed.magnitude = 0.0 65 | 66 | logic.mouse.position = .5, .5 67 | 68 | -------------------------------------------------------------------------------- /BGE/bghelper/input.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import math 4 | 5 | from bge import logic 6 | 7 | # input constants 8 | 9 | KEY = 0 # Different input types; Keyboard key 10 | JOYBUTTON = 1 # Joystick button 11 | JOYHAT = 2 # Joystick hat (D-Pad on a lot of controllers) 12 | JOYAXIS = 3 # Joystick axes (sticks and triggers on a 360 controller) 13 | MOUSEAXIS = 4 # Mouse movement axes 14 | MOUSEBUTTON = 5 # Mouse button 15 | 16 | STATE_UP = 0 17 | STATE_PRESSED = 1 18 | STATE_DOWN = 2 19 | STATE_RELEASED = 3 20 | 21 | # Joystick profiles. They are objects of classes, rather than dictionaries. This was done so that 22 | # it would be easier to work with when you're dealing with IDEs that can do code intelligence like Komodo Edit 23 | # Note that they start with JP, but these are US controllers, not Japanese ones, haha 24 | 25 | 26 | class _JP_GameCubeUSB(): 27 | """ 28 | Joystick profile for a GameCube controller connected via a USB adapter. 29 | Interestingly, the D-Pad can be detected as either buttons (the "Pad" ones below), or a standard Hat hat 30 | like most other joysticks (the Hat variables). 31 | 32 | The triggers are individual axes, unlike the 360's (which show up as a single axis). 33 | """ 34 | 35 | A = 1 36 | B = 2 37 | X = 0 38 | Y = 3 39 | 40 | L = 4 41 | R = 5 42 | Z = 7 43 | 44 | Start = 9 45 | 46 | Pad_Up = 12 # D-pad registers as buttons and a hat, for some reason 47 | Pad_Right = 13 48 | Pad_Down = 14 49 | Pad_Left = 15 50 | 51 | Hat_Up = 1 52 | Hat_Right = 2 53 | Hat_Down = 4 54 | Hat_Left = 8 55 | Hat_None = 0 56 | 57 | Stick_LeftHori = 0 58 | Stick_LeftVert = 1 59 | Stick_RightHori = 2 60 | Stick_RightVert = 3 61 | Trigger_Right = 4 62 | Trigger_Left = 5 63 | 64 | 65 | JPGamecubeUSB = _JP_GameCubeUSB() 66 | 67 | 68 | class _JP_PS2USB(): 69 | """ 70 | Joystick profile for a PS2 controller connected via a USB adapter. 71 | """ 72 | 73 | Triangle = 0 74 | Circle = 1 75 | Cross = 2 76 | Square = 3 77 | 78 | L2 = 4 79 | R2 = 5 80 | L1 = 6 81 | R1 = 7 82 | Select = 8 83 | Start = 9 84 | L3 = 10 85 | R3 = 11 86 | 87 | Hat_Up = 1 88 | Hat_Right = 2 89 | Hat_Down = 4 90 | Hat_Left = 8 91 | Hat_None = 0 92 | 93 | Stick_LeftHori = 0 94 | Stick_LeftVert = 1 95 | Stick_RightHori = 3 96 | Stick_RightVert = 2 97 | 98 | 99 | JPPS2USB = _JP_PS2USB 100 | 101 | 102 | class _JP_Chillstream(): 103 | """ 104 | Joystick profile for a Logitech Chillstream controller. 105 | """ 106 | 107 | Triangle = 3 108 | Circle = 2 109 | Cross = 1 110 | Square = 0 111 | 112 | L2 = 6 113 | R2 = 7 114 | L1 = 4 115 | R1 = 5 116 | Select = 8 117 | Start = 9 118 | L3 = 10 119 | R3 = 11 120 | 121 | Hat_Up = 1 122 | Hat_Right = 2 123 | Hat_Down = 4 124 | Hat_Left = 8 125 | Hat_None = 0 126 | 127 | Stick_LeftHori = 0 128 | Stick_LeftVert = 1 129 | Stick_RightHori = 2 130 | Stick_RightVert = 3 131 | 132 | 133 | JPChillstream = _JP_Chillstream 134 | 135 | 136 | class _JP_Xbox360(): 137 | """ 138 | Joystick profile for an XBOX 360 or otherwise X-input controller. 139 | 140 | Note that the triggers are implemented as a single axis; the controls don't seem to add up correctly (i.e. 141 | LT = axis 5 < 0, RT = axis 5 > 0; added together, they approach 0...). There's no alternative way to tell 142 | which axis is being pressed, either. :1 143 | 144 | Note that this should be the profile used for X-input controllers, even if they aren't XBOX 360 controllers, 145 | like the Logitech F310. 146 | """ 147 | 148 | A = 0 149 | B = 1 150 | X = 2 151 | Y = 3 152 | LB = 4 153 | RB = 5 154 | Back = 6 155 | Start = 7 156 | LS = 8 157 | RS = 9 158 | 159 | Hat_Up = 1 160 | Hat_Right = 2 161 | Hat_Down = 4 162 | Hat_Left = 8 163 | Hat_None = 0 164 | 165 | Stick_LeftHori = 0 166 | Stick_LeftVert = 1 167 | Stick_RightHori = 4 168 | Stick_RightVert = 3 169 | 170 | Triggers = 2 171 | 172 | 173 | JPXbox360 = _JP_Xbox360() 174 | 175 | 176 | # input classes 177 | 178 | class InputKey(): 179 | """ 180 | Tests for input from keyboard and joystick. 181 | """ 182 | 183 | def __init__(self, inputtype, keycode, axisdirection=1, deadzone=0.1, joyindex=0, scalar=1.0): 184 | 185 | """ 186 | A input handling object. 187 | 188 | inputtype = input type from CinputKey (CinputKey.KEYDOWN, for example) 189 | keycode = key, axis, hat, or button code for the input device (for example, events.XKEY, 5 for the fifth button 190 | on a joystick, etc.) 191 | axisdirection = direction to check for axis values for joystick and mouse axis checks. 192 | deadzone = percentage to disregard joystick movement. 193 | joyindex = joystick index to check. 194 | scalar = how large the end number is. Defaults to 1.0. This is useful to make corresponding inputs match up 195 | (i.e. make the mouse move at the same rate as the right analog stick). 196 | """ 197 | 198 | self.inputtype = inputtype 199 | self.keycode = keycode 200 | 201 | self.prevstate = 0 202 | self.prevpos = [0.0, 0.0] # Previous position of the mouse 203 | self.active = 0.0 204 | self.state = STATE_UP # The state of the key input (just pressed, released, down, up, etc.) 205 | 206 | self.scalar = scalar 207 | self.axisdirection = axisdirection # Default for axis checking 208 | self.deadzone = deadzone # Deadzone amount for axis checking 209 | self.joyindex = joyindex # Defaults to the first one 210 | 211 | def poll(self): 212 | 213 | """ 214 | Polls the input to check whether it's active or not. 215 | """ 216 | 217 | joy = logic.joysticks[self.joyindex] 218 | 219 | if self.inputtype == KEY: 220 | 221 | if logic.keyboard.events[self.keycode] == logic.KX_INPUT_ACTIVE: 222 | 223 | self.active = self.scalar 224 | 225 | else: 226 | 227 | self.active = 0.0 228 | 229 | elif self.inputtype == MOUSEAXIS: 230 | 231 | av = logic.mouse.position[self.keycode] - self.prevpos[self.keycode] 232 | 233 | self.state = STATE_DOWN 234 | 235 | if math.copysign(1, av) == self.axisdirection and abs(av) > self.deadzone: 236 | 237 | self.active = abs(av) * self.scalar 238 | 239 | else: 240 | 241 | self.active = 0.0 242 | 243 | self.prevpos = [0.5, 0.5] 244 | 245 | elif self.inputtype == MOUSEBUTTON: 246 | 247 | if logic.mouse.events[self.keycode] == logic.KX_INPUT_ACTIVE: 248 | 249 | self.active = self.scalar 250 | 251 | else: 252 | 253 | self.active = 0.0 254 | 255 | elif joy is not None and joy.numAxis and joy.numButtons and joy.numHats: 256 | 257 | if self.inputtype == JOYBUTTON: 258 | 259 | if self.keycode in joy.activeButtons: 260 | 261 | self.active = self.scalar 262 | 263 | else: 264 | 265 | self.active = 0.0 266 | 267 | elif self.inputtype == JOYHAT: 268 | 269 | hat = joy.hatValues[0] # TODO: Expand this to fit more than one hat 270 | 271 | if hat & self.keycode: 272 | 273 | self.active = self.scalar 274 | 275 | else: 276 | 277 | self.active = 0.0 278 | 279 | elif self.inputtype == JOYAXIS: 280 | 281 | av = joy.axisValues[self.keycode] 282 | 283 | pressed = abs(av) > self.deadzone and math.copysign(1, av) == self.axisdirection 284 | 285 | if pressed: 286 | 287 | self.active = abs(av) * self.scalar 288 | 289 | else: 290 | 291 | self.active = 0.0 292 | 293 | if self.active and not self.prevstate: 294 | 295 | self.state = STATE_PRESSED 296 | 297 | elif self.active and self.prevstate: 298 | 299 | self.state = STATE_DOWN 300 | 301 | elif not self.active and self.prevstate: 302 | 303 | self.state = STATE_RELEASED 304 | 305 | else: 306 | 307 | self.state = STATE_UP 308 | 309 | self.prevstate = self.active 310 | 311 | 312 | class InputDevice(): 313 | """ 314 | A class for testing for input from different devices. Useful if you want to easily set up different bindings 315 | for your input. 316 | 317 | Basically, you add the inputs via their inputtype (retrieved from CinputKey's input type constant definitions) 318 | with the keycode that you need. 319 | 320 | You can also specify a group for each binding entry (i.e. you can add keyboard and joystick controls separately.) 321 | 322 | Then, you poll the device with the group that you specify (so you can easily switch between key setups, and so it 323 | won't matter which device you use). 324 | 325 | Finally, you can easily retrieve the key / button / axis being pressed by just checking the device's events 326 | dictionary: 327 | 328 | ------------- 329 | 330 | device = InputDevice() 331 | 332 | device.add('jump', KEY, events.ZKEY, 'keyboard') 333 | 334 | device.poll('keyboard') 335 | 336 | print (device.bind_down('jump')) 337 | 338 | ------------- 339 | 340 | Note that you can also not specify a group if you want all input devices to work at the same time 341 | (i.e. use the joystick or the keyboard at the same time.) 342 | 343 | """ 344 | 345 | def __init__(self): 346 | 347 | """ 348 | Example code usage: 349 | 350 | device = InputDevice() 351 | 352 | device.Add('jump', BGinput.KEYPRESSED, events.ZKEY, 'keyboard') 353 | 354 | device.Poll('keyboard') 355 | 356 | print (device.bindings['jump']) 357 | 358 | """ 359 | 360 | self.events = {} 361 | self.bindings = {} 362 | self.states = {} # The states of individual bindings (seemed easier than having to use bindings['active'] and bindings['state']) 363 | 364 | def add(self, bindingname, inputtype, keycode, axisdir=1, deadzone=0.1, joyindex=0, scalar=1.0, group="default"): 365 | 366 | """ 367 | Add a key binding. 368 | 369 | bindingname = name of the binding to create (i.e. 'left', 'jump', 'run', 'interact'...) 370 | inputtype = input type from CinputKey (CinputKey.KEYDOWN, for example) 371 | keycode = key, axis, hat, or button code for the input device (for example, events.XKEY, 5 for the fifth button on a joystick, etc.) 372 | 373 | group = a string that designates what group to add this binding to. For example, you might add all of the joystick 374 | bindings to a 'joystick' group, and all of the keyboard bindings to a 'keyboard' group 375 | 376 | axisdir = direction to check for axis values for joystick and mouse axis checks. 377 | deadzone = Fraction to disregard joystick movement. 378 | joyindex = joystick index to check. 379 | scalar = how large the end number is. Defaults to 1.0. This is useful to make corresponding inputs match up (i.e. make 380 | the mouse move at the same rate as the right analog stick). 381 | 382 | """ 383 | 384 | if not group in self.events: 385 | self.events[group] = {} 386 | 387 | if not bindingname in self.events[group]: 388 | # if not group in self.events: # Set up the events dictionary 389 | 390 | # self.events[group] = {} 391 | 392 | self.events[group][bindingname] = [] 393 | 394 | self.bindings[bindingname] = {'active': 0.0, 'type': KEY, 'state': 0} 395 | 396 | self.events[group][bindingname].append(InputKey(inputtype, keycode, axisdir, deadzone, joyindex, scalar)) 397 | 398 | def poll(self, group=None): 399 | 400 | """ 401 | Poll the bindings for updates. 402 | 403 | group = which set of bindings to poll in particular. If left to None, then it will poll all groups specified. 404 | If you specify, then it will poll that one in particular. Useful for switching input schemes. 405 | """ 406 | 407 | if group is None: 408 | 409 | poll_groups = [gr for gr in self.events] 410 | 411 | else: 412 | 413 | poll_groups = [group] 414 | 415 | for group in poll_groups: 416 | 417 | for binding in self.events[group]: 418 | 419 | self.bindings[binding] = {'active': 0.0, 'type': None, 'state': 0} 420 | 421 | for input_o in self.events[group][binding]: 422 | 423 | input_o.poll() 424 | 425 | if input_o.active: 426 | 427 | self.bindings[binding] = {'active': input_o.active, 'state': input_o.state, 428 | 'type': input_o.inputtype} 429 | 430 | else: 431 | 432 | if input_o.state == STATE_RELEASED: 433 | self.bindings[binding]['state'] = input_o.state 434 | 435 | def type_active(self, input_type): 436 | 437 | """ 438 | Checks to see if a certain kind of input type is active. 439 | """ 440 | 441 | for b in self.bindings: 442 | 443 | if self.bindings[b]['type'] == input_type and self.bindings[b]['active']: 444 | return 1 445 | 446 | return 0 447 | 448 | def bind_down(self, bind): 449 | """ 450 | Checks to see if the binding you specify is activated currently (down). 451 | 452 | bind = binding name 453 | """ 454 | 455 | return self.bindings[bind]['active'] if self.bindings[bind]['state'] == STATE_DOWN else 0.0 456 | 457 | def bind_pressed(self, bind): 458 | """ 459 | Checks to see if the binding you specify was just pressed this frame. 460 | 461 | bind = binding name 462 | """ 463 | return self.bindings[bind]['active'] if self.bindings[bind]['state'] == STATE_PRESSED else 0.0 464 | 465 | def bind_released(self, bind): 466 | """ 467 | Checks to see if the binding you specify was just released this frame. If it was, 1 is returned. 468 | 469 | bind = binding name 470 | """ 471 | return 1.0 if self.bindings[bind]['state'] == STATE_RELEASED else 0.0 472 | 473 | device = InputDevice() 474 | -------------------------------------------------------------------------------- /BGE/bghelper/light.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic 4 | 5 | LT_POINT = 0 6 | LT_SPOT = 1 7 | LT_SUN = 2 8 | LT_HEMI = 3 9 | 10 | 11 | class LightManager(): 12 | """ 13 | 14 | A class for managing lights. 15 | 16 | The Poll() function looks through the scene to find objects that have a property named "DynLightNode", which indicates 17 | spots where lights should be dynamically. 18 | 19 | The Update() function spawns and places dynamic lights to these light node positions. 20 | 21 | TODO: Expand the Update() function to copy the nodes' energy, color, and other light properties if they're outlined 22 | in the group object (if it is one), and otherwise in the object itself. 23 | 24 | """ 25 | 26 | def __init__(self): 27 | 28 | self.light_nodes = {'point': [], 29 | 'sun': [], 30 | 'spot': [], 31 | } # [s for s in sce.objects if 'DynLightNode' in s] 32 | self.template_lights = {'point': [], 33 | 'sun': [], 34 | 'spot': [], 35 | } 36 | self.existing_lights = {'point': [], 37 | 'sun': [], 38 | 'spot': [], 39 | } 40 | 41 | self.default_energy = 2.0 # Default values for lights that can be overridden by 42 | # putting values in the dynamic light polling objects themselves (TODO: Actually implement that) 43 | 44 | self.default_distance = 10.0 45 | 46 | self.default_color = [1, 1, 1] 47 | 48 | self.default_spotsize = 45 49 | 50 | self.default_spotblend = 0.25 51 | 52 | def update(self, ref_obj=None, scene=None): 53 | 54 | """ 55 | Create new lights from existing "template" lights and move them as necessary to the nodes closest to the ref_point. 56 | """ 57 | 58 | cont = logic.getCurrentController() 59 | obj = cont.owner 60 | 61 | if scene == None: 62 | sce = logic.getCurrentScene() 63 | else: 64 | sce = scene 65 | 66 | if ref_obj == None: 67 | point = sce.active_camera 68 | else: 69 | point = ref_obj 70 | 71 | for l in self.light_nodes: 72 | 73 | if len(self.existing_lights[l]) == 0: 74 | 75 | for x in range(len(self.template_lights[l][:])): 76 | light = sce.addObject(self.template_lights[l].pop(), obj) 77 | 78 | light.energy = 0.0 79 | 80 | self.existing_lights[l].append(light) 81 | 82 | nodes = self.light_nodes[l][:] 83 | nodes.sort(key=lambda x: point.getDistanceTo(x)) 84 | 85 | for n in range(len(nodes)): 86 | 87 | node = nodes[n] 88 | 89 | if n <= len(self.existing_lights[l]) - 1: 90 | cur_light = self.existing_lights[l][n] 91 | 92 | cur_light.worldPosition = node.worldPosition 93 | cur_light.worldOrientation = node.worldOrientation 94 | 95 | cur_light.energy = self.default_energy # TODO: Link these to the node objects or their group objects if possible 96 | 97 | def poll(self, scene=None): 98 | 99 | """ 100 | Polls the scene for light nodes, and polls the inactive layer for template lights 101 | """ 102 | 103 | if scene == None: 104 | sce = logic.getCurrentScene() 105 | else: 106 | sce = scene 107 | 108 | for s in sce.objects: 109 | 110 | if "DynLightNode" in s: 111 | 112 | if s['DynLightNode'].lower() == 'point': 113 | self.light_nodes['point'].append(s) 114 | elif s['DynLightNode'].lower() == 'sun': 115 | self.light_nodes['sun'].append(s) 116 | elif s['DynLightNode'].lower() == 'spot': 117 | self.light_nodes['spot'].append(s) 118 | 119 | for s in sce.objectsInactive: 120 | 121 | if 'DynLight' in s: 122 | 123 | if s.type == s.NORMAL: 124 | self.template_lights['point'].append(s) 125 | elif s.type == s.SUN: 126 | self.template_lights['sun'].append(s) 127 | elif s.type == s.SPOT: 128 | self.template_lights['spot'].append(s) 129 | 130 | 131 | def add_light(light_type=LT_POINT, time=0, priority=0): 132 | """ 133 | Spawns a light that was pre-placed in a hidden layer. Note that the light must have a property present 134 | in it named "DynLight". It must be set to "Point", "Sun", "Hemi", or "Spot" for it to spawn in correctly. 135 | 136 | time = how long the light should stick around in frames 137 | priority = what priority the light should spawn with. Higher priorities will delete and re-create 138 | (overwrite, if you will) existing lights if not enough pre-made lights are present. 139 | 140 | Basically, priority allows you to spawn lights with low 141 | priority at non important places, like spawn 10 torches with lights at priority 0, and then spawn a light 142 | for the player with a priority of 1. Since it has a higher priority, the player's light will spawn, and one 143 | of the torches' lights will be deleted. 144 | """ 145 | 146 | sce = logic.getCurrentScene() 147 | 148 | obj = logic.getCurrentController().owner 149 | 150 | if not hasattr(logic, 'lights_used'): 151 | 152 | logic.lights_used = { 153 | # 'Point':{}, 154 | # 'Sun':{}, 155 | #'Hemi':{}, 156 | #'Spot':{}, 157 | } # Create a mapping of lamps with their types to spawners 158 | 159 | # for lt in logic.lights_used: 160 | 161 | for l in [l for l in sce.objectsInactive if 162 | "DynLight" in l]: # Loop through the lamps that have the string "Dyn" + the type in their name (i.e. Point lamps named "Point", "Point.001", etc.) 163 | 164 | if not l['DynLight'] in logic.lights_used: 165 | logic.lights_used[l['DynLight']] = {} 166 | 167 | logic.lights_used[l['DynLight']][l.name.lower()] = {'reference': None, 168 | 'priority': 0} # And set their "used" status to 0 169 | 170 | if light_type == LT_POINT: 171 | lt = 'point' 172 | elif light_type == LT_SPOT: 173 | lt = 'spot' 174 | elif light_type == LT_SUN: 175 | lt = 'sun' 176 | else: 177 | lt = 'hemi' 178 | 179 | for l in logic.lights_used[lt]: # Then loop through all of the lamps 180 | 181 | light_dict = logic.lights_used[lt][l] 182 | 183 | if light_dict['reference'] == None: # And when you find a lamp that hasn't been used 184 | 185 | light = sce.addObject(l, obj, time) # Create that lamp, 186 | 187 | light_dict['priority'] = priority 188 | 189 | light_dict[ 190 | 'reference'] = light # And set it to be used (mitts off to all other spawners until it's destroyed) 191 | 192 | return light # And then break out of the loop, returning the spawned light 193 | 194 | else: 195 | 196 | if light_dict['reference'].invalid: 197 | # If the lamp you've "found" is used, but doesn't exist or has been deleted, or has a lower priority 198 | # than intended, 199 | 200 | light = sce.addObject(l, obj, time) 201 | 202 | light_dict['reference'] = light # Then re-use that one, rather than looking for another lamp 203 | 204 | light_dict['priority'] = priority 205 | 206 | return light # And then break out of the loop, returning the spawned light 207 | 208 | light_keys = list(logic.lights_used[lt].keys()) 209 | 210 | lights_pri = sorted(light_keys, key=lambda light_in_use: logic.lights_used[lt][light_in_use]['priority']) 211 | 212 | for l in lights_pri: 213 | 214 | light_dict = logic.lights_used[lt][l] 215 | 216 | if light_dict['priority'] < priority: 217 | light_dict['reference'].endObject() 218 | 219 | light = sce.addObject(l, obj, time) 220 | 221 | light_dict['reference'] = light 222 | 223 | light_dict['priority'] = priority 224 | 225 | return light 226 | 227 | return None 228 | -------------------------------------------------------------------------------- /BGE/bghelper/math.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import mathutils 4 | 5 | from bge import logic, render 6 | 7 | 8 | def lerp(a, b, scalar): 9 | """Lerp - Linearly interpolates between 'a' 10 | when 'scalar' is 0 and 'b' when 'scalar' is 1. 11 | a = number or Vector 12 | b = number or Vector 13 | scaler = number between 0 and 1 14 | """ 15 | return (a + scalar * (b - a)) 16 | 17 | 18 | def clamp(value, minimum, maximum): 19 | """ 20 | Clamp: Clamps the specified 'value' between the maximum and minimum values. 21 | Returns 'max' when 'value' is greater than 'max', 'min' when 'value' is less than 'min', 22 | and 'value' itself when neither is true. 23 | """ 24 | return (min(max(value, minimum), maximum)) 25 | 26 | 27 | def wrap(value, minimum, maximum, preserve_remainder=True): 28 | 29 | if value < minimum: 30 | 31 | value = maximum 32 | 33 | if preserve_remainder: 34 | 35 | value -= abs(value - minimum) 36 | 37 | elif value > maximum: 38 | 39 | value = minimum 40 | 41 | if preserve_remainder: 42 | 43 | value += abs(value - maximum) 44 | 45 | return value 46 | 47 | 48 | def sign(value): 49 | """ 50 | Returns the sign of the inputted 'value'; i.e. 5 = 1, -9.1 = -1, 0 = 0 51 | """ 52 | if value > 0: 53 | return 1 54 | elif value < 0: 55 | return -1 56 | else: 57 | return 0 58 | 59 | 60 | def sort_closest_gameobjects(first, others): 61 | 62 | l = others[:] 63 | 64 | l.sort(key=lambda x: first.getDistanceTo(x)) 65 | 66 | return l 67 | 68 | 69 | def sort_closest_vectors(first, others): 70 | 71 | l = others[:] 72 | 73 | l.sort(key=lambda x: (first - x).magnitude) 74 | 75 | return l 76 | 77 | 78 | def raycast_line(anglevect, anglewidth, topos, frompos=None, raynum=3, 79 | center=1, from_scalar=1.0, to_scalar=1.0, dist=0, prop='', face=1, xray=1, 80 | poly=0, obj=None, debug=False, objdebug=None): 81 | """ 82 | Casts several rays in a line, starting from frompos and going to topos, and then iterating along the line indicated by anglevect. 83 | 84 | The function returns a dictionary, consisting of four items: 85 | 86 | 'rays': The return of the raycasts (either a hit, or an empty list, depending on what the rays hit; the output from a raycast() function, basically) 87 | 'ray_end': The ending position of the last successful raycast, or None if there wasn't a successful raycast 88 | 'ray_start' The starting position of the last successful raycast, or None if there wasn't a successful raycast 89 | 'offset' The offset between the starting point of the successful ray cast and the provided starting ray cast position. Useful in case 90 | you want to reorient the position of an object from the ray cast (i.e. move to contact point), but still want to use the object's 91 | center position (don't move to where the ray hit, but move to where it hit relative to the object's center position). 92 | 93 | anglevect = The angle that the rays should be cast on. 94 | 95 | anglewidth = How wide the rays should be cast in Blender Units (i.e. a value of 1 means that from the left-most ray to the right-most ray is 1 BU). 96 | 97 | raynum = number of rays to cast 98 | 99 | center = if the rays should be centered on the anglevect Vector or not 100 | (i.e. treat frompos and topos as the center ray, or as the ray starting from anglevect) 101 | 102 | from / to_scalar = A percentage of the width to stretch (i.e. to have a wider end "edge" than starting "edge") 103 | 104 | dist = distance of each raycast; defaults to 0, which equals the distance between the from position and end position of the vectors 105 | 106 | prop = property to check for with each raycast; defaults = '', which detects any object 107 | 108 | face = whether to return a face if the ray hits something 109 | 110 | xray = whether to go through objects that don't match the property criteria (objects that don't have the property 111 | named by 'prop') 112 | 113 | poly = whether to return the polygon / UV hit; check the api documentation for more information 114 | 115 | obj = object to use for the ray casts; if left set to default (None), then it will use whatever object is calling 116 | the function. 117 | 118 | objdebug = default None; an object to use for debugging 119 | 120 | ----- 121 | 122 | Okay, so now what is this actually used for? Mainly, to do kind of a 'ray cast wall'. An example would be if you 123 | want to cast multiple rays for a character in a platforming game. You would do this instead of using the built-in Bullet 124 | physics engine to handle gravity and collisions so that you could have partially impassable objects, for example. 125 | 126 | An example of using this function would look something like this: 127 | 128 | width = 0.6 129 | 130 | frompos = obj.worldPosition.copy() 131 | topos = frompos.copy() 132 | topos.z -= 1 133 | 134 | dist = 0.5 + abs(obj.worldLinearVelocity.z) 135 | 136 | ground = bghelper.RaycastLine(obj.worldOrientation.col[0], width, topos, frompos, 3, 1, dist, 'ground', debug = 1)[0] 137 | 138 | This will cast three rays in a straight line, with the center one being below the object's world position. Debug is on, 139 | so it will draw the lines visibly onscreen (barring a bug with the render engine about drawing lines with overlay or 140 | underlay scenes activated). It will return an object with the 'ground' property, if it finds one. If so, then it will 141 | return a list consisting of the successful raycast and its starting and ending position (i.e. [ray, topos, frompos]). 142 | Otherwise, it will return a list consisting of a list with a None in it, and two other Nones (i.e. [[None], None, None]). 143 | This way you can check: if bghelper.LineRayCast()[0] == None to see if the ray was successful (or any other index of the 144 | returned list). 145 | 146 | """ 147 | 148 | anglevect = anglevect.copy() 149 | anglevect.magnitude = anglewidth 150 | 151 | if obj is None: 152 | obj = logic.getCurrentController().owner 153 | if frompos is None: 154 | frompos = obj.worldPosition.copy() 155 | 156 | if not isinstance(topos, mathutils.Vector): 157 | topos = mathutils.Vector(topos) 158 | if not isinstance(frompos, mathutils.Vector): 159 | frompos = mathutils.Vector(frompos) 160 | 161 | rtp = topos.copy() 162 | rfp = frompos.copy() 163 | 164 | rn = raynum - 1 165 | 166 | if rn <= 0: 167 | rn = 1 168 | 169 | ray = [None] 170 | 171 | if center: 172 | rtp -= (anglevect / 2) * to_scalar 173 | rfp -= (anglevect / 2) * from_scalar 174 | 175 | output = {} 176 | 177 | for x in range(raynum): 178 | 179 | ray = obj.rayCast(rtp, rfp, dist, prop, face, xray, poly) 180 | 181 | if debug: 182 | 183 | to = rtp - rfp 184 | to.magnitude = dist 185 | 186 | if ray[0]: 187 | render.drawLine(rfp, rfp + to, [0, 1, 0]) 188 | else: 189 | render.drawLine(rfp, rfp + to, [1, 0, 0]) 190 | 191 | if objdebug: 192 | 193 | to = rtp - rfp 194 | 195 | sce = logic.getCurrentScene() 196 | 197 | db = sce.addObject(objdebug, obj, 1) 198 | db.worldPosition = rfp 199 | db.alignAxisToVect(to, 0) 200 | db.worldScale = [to.magnitude, 1, 1] 201 | 202 | if ray[0]: 203 | db.color = [0, 1, 0, 1] 204 | else: 205 | db.color = [1, 0, 0, 1] 206 | 207 | if ray[0] is not None: 208 | 209 | if output == {}: 210 | output["rays"] = ray 211 | output["ray_start"] = rfp 212 | output["ray_end"] = rtp 213 | output["offset"] = rfp - frompos 214 | else: 215 | 216 | if (rfp - ray[1]).magnitude < (output['ray_end'] - output['rays'][1]).magnitude: 217 | # Only overwrite previous raycast results if this one is closer 218 | output["rays"] = ray 219 | output["ray_start"] = rfp 220 | output["ray_end"] = rtp 221 | output["offset"] = rfp - frompos 222 | 223 | av = anglevect / rn 224 | 225 | rtp += av * to_scalar 226 | rfp += av * from_scalar 227 | 228 | # if not converge: 229 | # rfp += av 230 | 231 | if output: 232 | return output 233 | 234 | return {'rays': ray, 'ray_start': None, 'ray_end': None, 'offset': None} -------------------------------------------------------------------------------- /BGE/bghelper/nodemap.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic, render 4 | 5 | import math,time 6 | 7 | # Nodemaps for game space navigation. 8 | 9 | 10 | class Node(): 11 | 12 | def __init__(self, obj): 13 | 14 | self.obj = obj 15 | 16 | self.obj['nm_node'] = self # Place yourself in the "owner" game object 17 | 18 | self.position = self.obj.worldPosition 19 | 20 | self.neighbors = [] 21 | 22 | #self.cost = 1 23 | 24 | self.costs = [] 25 | 26 | self.parent = None # Parent node for path-finding 27 | 28 | #self.g_score = 0 29 | #self.f_score = 0 30 | #self.h_score = 0 31 | 32 | def reset_costs(self, cost_num): 33 | 34 | self.costs = [0 for x in range(cost_num)] 35 | 36 | def set_cost(self, value, cost_index=0): 37 | 38 | self.costs[cost_index] = value 39 | 40 | def get_cost(self, cost_index=0): 41 | 42 | return self.costs[cost_index] 43 | 44 | def add_cost(self, value, cost_index=0): 45 | 46 | self.costs[cost_index] += value 47 | 48 | def sub_cost(self, value, cost_index=0): 49 | 50 | self.costs[cost_index] -= value 51 | 52 | 53 | class Path(): 54 | 55 | def __init__(self, path): 56 | 57 | self.points = path[:] 58 | self.temp_points = self.points[:] 59 | 60 | def on_reached_end(self): 61 | 62 | pass 63 | 64 | def navigate(self, turn_spd=0.0, accel_spd=1.0, max_spd=10.0, friction=1.0, close_enough=0.5, obj=None): 65 | 66 | if obj is None: 67 | 68 | obj = logic.getCurrentController().owner 69 | 70 | else: 71 | 72 | obj = obj 73 | 74 | if len(self.temp_points) > 0: 75 | 76 | next_point = self.temp_points[0].obj.worldPosition 77 | 78 | next_dir = obj.getVectTo(next_point)[1] 79 | 80 | vel = obj.worldLinearVelocity.copy() 81 | 82 | vel.z = 0.0 83 | 84 | vel.magnitude -= friction if vel.magnitude > friction else vel.magnitude 85 | 86 | vel.xy += next_dir.xy * (accel_spd + friction) 87 | 88 | if (obj.worldLinearVelocity.xy + vel.xy).magnitude < (max_spd): 89 | 90 | obj.worldLinearVelocity.xy = vel.xy 91 | 92 | if (obj.worldPosition - next_point).magnitude < close_enough: 93 | 94 | self.temp_points.pop(0) 95 | 96 | else: 97 | 98 | print('done') 99 | 100 | self.on_reached_end() 101 | 102 | vel = obj.worldLinearVelocity.copy() 103 | vel.magnitude -= friction if vel.magnitude > friction else vel.magnitude 104 | obj.worldLinearVelocity.xy = vel.xy 105 | 106 | def reset_path(self): 107 | 108 | self.temp_points = self.points[:] 109 | 110 | 111 | class NodeMap(): 112 | 113 | def __init__(self, cost_num=1): 114 | 115 | self.nodes = [] 116 | 117 | self.cost_num = cost_num # Number of costs per node; useful if you need multiple 118 | # costs for different values (terrain, risk, wants, dislikes, etc) 119 | 120 | def add_node(self, node): 121 | 122 | """ 123 | Adds the node to the nodemap. Also adds cost slots to the node. 124 | :param node: 125 | :return: 126 | """ 127 | 128 | node.reset_costs(self.cost_num) 129 | 130 | self.nodes.append(node) 131 | 132 | def remove_node(self, node): 133 | 134 | self.nodes.remove(node) 135 | 136 | def update_neighbors(self, min_dist=0, max_dist=9999, max_connections=9999): 137 | 138 | cont = logic.getCurrentController() 139 | obj = cont.owner 140 | 141 | evaluated = [] 142 | 143 | for n in self.nodes: 144 | 145 | for m in self.nodes: 146 | 147 | if n != m and not m in evaluated: 148 | 149 | d = (n.position - m.position).magnitude 150 | 151 | if max_dist >= d >= min_dist: 152 | 153 | ray = obj.rayCast(n.position, m.position, 0, 'nm_solid', 1, 1) 154 | 155 | if not ray[0]: 156 | 157 | if len(n.neighbors) < max_connections and len(m.neighbors) < max_connections: 158 | 159 | n.neighbors.append(m) 160 | m.neighbors.append(n) 161 | 162 | evaluated.append(n) 163 | 164 | def get_closest_node(self, position): 165 | 166 | cont = logic.getCurrentController() 167 | 168 | obj = cont.owner 169 | 170 | sorted_nodes = self.nodes[:] 171 | 172 | sorted_nodes.sort(key=lambda n: (n.position - position).magnitude) 173 | 174 | closest = None 175 | 176 | for n in sorted_nodes: 177 | 178 | ray = obj.rayCast(n.position, position, 0, 'nm_solid', 1, 1, 1) 179 | 180 | if not ray[0]: 181 | 182 | closest = n 183 | 184 | break 185 | 186 | return closest 187 | 188 | def get_path_to(self, ending_point, starting_point=None, max_check_num=1000, cost_coefficient=None): 189 | 190 | if cost_coefficient is None: 191 | 192 | cost_coefficient = [1 for x in range(self.cost_num)] 193 | 194 | if starting_point is None: 195 | 196 | starting_point = logic.getCurrentController().owner.worldPosition.copy() 197 | 198 | goal = self.get_closest_node(ending_point) 199 | starting_node = self.get_closest_node(starting_point) 200 | 201 | def get_f_score(node): 202 | 203 | costs = [x*y for x,y in zip(node.costs, cost_coefficient)] 204 | 205 | return (starting_node.obj.position - node.obj.position).magnitude + (node.obj.position - goal.obj.position).magnitude + sum(costs) 206 | 207 | if not goal: 208 | 209 | print("ERROR: GOAL POSITION CANNOT BE REACHED FROM ANY NODE ON MAP.") 210 | return 211 | 212 | if not starting_node: 213 | 214 | print("ERROR: STARTING NODE CANNOT BE REACHED FROM ANY NODE ON MAP.") 215 | return 216 | 217 | open_list = [starting_node] 218 | closed_list = [] 219 | 220 | exit_loop = False 221 | 222 | for x in range(max_check_num): 223 | 224 | open_list.sort(key=get_f_score) 225 | 226 | current_node = open_list.pop(0) 227 | closed_list.append(current_node) 228 | 229 | for neighbor in current_node.neighbors: 230 | 231 | if neighbor in closed_list: 232 | 233 | continue 234 | 235 | if neighbor not in open_list: 236 | 237 | neighbor.parent = current_node 238 | 239 | open_list.append(neighbor) 240 | 241 | if neighbor == goal: 242 | 243 | exit_loop = True # A path has been found 244 | 245 | break 246 | 247 | if exit_loop: 248 | 249 | break 250 | 251 | path = [] 252 | 253 | target_square = goal 254 | 255 | for x in range(1000): 256 | 257 | path.append(target_square) 258 | 259 | if target_square == starting_node: 260 | 261 | break 262 | 263 | target_square = target_square.parent 264 | 265 | path.reverse() # Go from the start to the goal 266 | 267 | if len(path): 268 | 269 | return Path(path) # Create a path object 270 | 271 | else: 272 | 273 | print("No path found") 274 | 275 | def get_path_costs(self, path): 276 | 277 | costs = [0 for x in range(self.cost_num)] 278 | 279 | for node in path.points: 280 | 281 | costs = [x + y for x, y in zip(costs, node.costs)] 282 | 283 | return costs 284 | 285 | def debug_node_connections(self): 286 | 287 | for node in self.nodes: 288 | 289 | for neighbor in node.neighbors: 290 | 291 | render.drawLine(node.position, neighbor.position, [1, 0, 0]) 292 | 293 | def debug_node_path(self, path): 294 | 295 | #print("______") 296 | 297 | for node in path.points: 298 | 299 | #print(node.obj) 300 | 301 | s = abs(math.sin(time.clock() * math.pi)) 302 | 303 | node.obj.color = [s, s, s, 1] -------------------------------------------------------------------------------- /BGE/bghelper/sprites.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import math 4 | 5 | from bge import logic 6 | 7 | 8 | class _spritemap_base(): 9 | 10 | def __init__(self, obj): 11 | 12 | """ 13 | Creates a SpriteMap to animate an object by. 14 | 15 | :param obj: KX_GameObject - Sprite object to perform operations on 16 | :return: _spritemap_base 17 | """ 18 | 19 | self.obj = obj 20 | 21 | self.subimage = 1.0 22 | 23 | self.prev_subimage = 1.0 24 | 25 | self.fps_lock = True # Lock the animating process to the logic tic rate (i.e. target FPS) or not (use the 26 | # average frame-rate) 27 | 28 | self.adjust_by_sensor_frequency = True # Attempts to take into account the first sensor's frequency when 29 | # animating 30 | 31 | self.anim_dict = {} 32 | 33 | self.current_animation = None # The current animation 34 | 35 | self.prev_animation = None # The previous animation 36 | 37 | self.is_playing = False # If the sprite-map is currently playing an animation or is 38 | # stopped after playing a one-shot one 39 | 40 | def add(self, anim_name, animation, fps=15, loop=True): 41 | 42 | """ 43 | :param anim_name: Str - Name of the animation 44 | :param animation: List - A list "describing" the animation 45 | :param fps: Int - The FPS to play the animation back at 46 | :param loop: Bool - Whether the animation should loop or not 47 | :return: None 48 | 49 | Registers the animation specified to the SpriteMap. 50 | """ 51 | 52 | self.anim_dict[anim_name] = {'animation': animation, 'fps': fps, 'loop': loop} 53 | 54 | def remove(self, anim_name): 55 | """ 56 | :param anim_name: Str 57 | :return: Bool 58 | Removes the specified animation from the animations registered in the SpriteMap. Returns True if the animation 59 | existed, and False otherwise. 60 | """ 61 | 62 | if anim_name in self.anim_dict: 63 | del self.anim_dict[anim_name] 64 | return True 65 | else: 66 | return False 67 | 68 | def get_animation_names(self): 69 | """ 70 | :return: List 71 | Returns a list of the names of each animation in the SpriteMap. 72 | """ 73 | 74 | return [name for name in self.anim_dict] 75 | 76 | def get_animation_dict(self): 77 | """ 78 | :return: Dictionary 79 | Returns each animation registered in the SpriteMap. 80 | """ 81 | 82 | return self.anim_dict 83 | 84 | def set_fps(self, anim_name, fps): 85 | 86 | self.anim_dict[anim_name]['fps'] = fps 87 | 88 | def get_fps(self, anim_name): 89 | 90 | return self.anim_dict[anim_name]['fps'] 91 | 92 | def set_loop(self, anim_name, loop): 93 | """ 94 | Sets the loop value of the animation specified. 95 | :param anim_name: String - Name of the animation. 96 | :param loop: Bool - Whether to loop the animation when played or not. 97 | :return: None 98 | """ 99 | 100 | self.anim_dict[anim_name]['loop'] = loop 101 | 102 | def get_loop(self, anim_name): 103 | 104 | """ 105 | Returns the loop value of the animation specified. 106 | :param anim_name: String - Name of the animation to check. 107 | :return: None 108 | """ 109 | 110 | return self.anim_dict[anim_name]['loop'] 111 | 112 | def set_subimage(self, subimage): 113 | """ 114 | Sets the subimage of the spritemap. 115 | :param subimage: Float - Subimage to set; 1 is the start of the animation. 116 | :return:None 117 | """ 118 | 119 | self.subimage = float(subimage) 120 | 121 | def get_subimage(self): 122 | """ 123 | Returns the current subimage of the sprite map. 124 | :return:None 125 | """ 126 | 127 | return self.subimage 128 | 129 | def is_playing(self): 130 | """ 131 | Returns if the spritemap is playing an animation or not. Only is set to False if it's playing an animation that 132 | doesn't loop, and the animation has finished. 133 | :return:Bool 134 | """ 135 | 136 | return self.is_playing 137 | 138 | def has_animation(self, anim_name): 139 | 140 | """ 141 | Returns if the spritemap has an animation named defined. 142 | :param anim_name: String - Name of the animation 143 | :return: Bool 144 | """ 145 | 146 | return anim_name in self.anim_dict 147 | 148 | def play(self, anim_name, reset_subimage_on_change=True): 149 | 150 | if not anim_name in self.anim_dict: 151 | 152 | print("ERROR: SpriteMap owner " + self.obj.name + " has no animation named " + anim_name + ".") 153 | 154 | return False 155 | 156 | elif len(self.anim_dict[anim_name]['animation']) < 2: 157 | 158 | print("ERROR: SpriteMap owner " + self.obj.name + "'s animation " + anim_name + " does not contain data " 159 | "for at least one frame of " 160 | "animation.") 161 | 162 | return False 163 | 164 | self.current_animation = anim_name 165 | 166 | if self.current_animation != self.prev_animation: 167 | 168 | self.on_animation_change(self, self.current_animation, self.prev_animation) # Callback 169 | 170 | if reset_subimage_on_change: 171 | self.subimage = 1.0 172 | 173 | if self.adjust_by_sensor_frequency: 174 | freq = logic.getCurrentController().sensors[0].frequency + 1 175 | else: 176 | freq = 1 177 | 178 | if self.fps_lock: 179 | scene_fps = logic.getLogicTicRate() 180 | else: 181 | scene_fps = logic.getAverageFrameRate() 182 | if scene_fps == 0.0: 183 | scene_fps = logic.getLogicTicRate() 184 | 185 | if anim_name and anim_name in self.anim_dict: 186 | 187 | anim = self.anim_dict[anim_name]['animation'] 188 | anim_fps = self.anim_dict[anim_name]['fps'] 189 | 190 | target_fps = anim_fps / scene_fps 191 | 192 | if len(anim) > 2: # Don't advance if there's only one cell to the animation 193 | 194 | if target_fps != 0.0: 195 | self.subimage += round(target_fps, 2) * freq 196 | 197 | subi = round(self.subimage, 4) 198 | 199 | if (self.subimage <= len(anim) - 2 and anim_fps > 0) or (self.subimage > 1.0 and anim_fps < 0): 200 | self.is_playing = True 201 | 202 | if self.anim_dict[anim_name]['loop']: # The animation is to be looped 203 | 204 | fin = 0 205 | 206 | if len(anim) >= 2: 207 | 208 | while math.floor(subi) > len(anim) - 1: 209 | subi -= len(anim) - 1 210 | fin = 1 211 | 212 | while math.floor(subi) < 1.0: 213 | subi += len(anim) - 1 214 | fin = 1 215 | 216 | if fin: 217 | self.on_animation_finished(self) # Animation's finished because it's looping 218 | 219 | self.is_playing = True 220 | 221 | else: # No loop 222 | 223 | if subi >= len(anim) - 1 or subi < 1.0: 224 | 225 | if self.is_playing: 226 | self.on_animation_finished(self) # Animation's finished because it's at the end of a 227 | # non-looping animation 228 | 229 | if len(anim) >= 2: 230 | subi = min(max(subi, 1.0), len(anim) - 1) 231 | else: 232 | subi = 1.0 233 | 234 | self.is_playing = False 235 | 236 | if math.floor(subi) != math.floor(self.prev_subimage): 237 | self.on_subimage_change(self, math.floor(subi), math.floor(self.prev_subimage)) 238 | # Callback for subimage change 239 | 240 | self.subimage = subi 241 | 242 | else: 243 | 244 | print("ERROR: ANIMATION NAME " + anim_name + " NOT IN SPRITEMAP OR NOT STRING.") 245 | 246 | self.prev_animation = self.current_animation 247 | self.prev_subimage = self.subimage 248 | 249 | if self.obj.invalid: # You did something that killed the object; STOP PRODUCTION! 250 | 251 | print("ERROR: SPRITE MAP OPERATING ON NON-EXISTENT OBJECT!!!") 252 | 253 | return False 254 | 255 | return True 256 | 257 | def get_current_animation(self): 258 | """ 259 | Returns the name of the current animation playing. 260 | :return: String 261 | """ 262 | 263 | return self.current_animation 264 | 265 | def get_animation_length(self, anim_name): 266 | """ 267 | Returns the length of the animation specified. 268 | :param anim_name: String - Name of the animation 269 | :return: Int 270 | """ 271 | 272 | return len(self.anim_dict[anim_name]['animation']) - 1 273 | 274 | def copy_animations(self, other_spritemap): 275 | 276 | self.anim_dict = other_spritemap.anim_dict.copy() 277 | 278 | def on_last_frame(self): 279 | 280 | return self.subimage >= self.get_animation_length(self.current_animation) 281 | 282 | # CALLBACKS 283 | 284 | def on_subimage_change(self, sprite_map, current_subimage, prev_subimage): 285 | 286 | """ 287 | Stub callback that runs when the animation's subimage changes (i.e. the animation changes frames). 288 | Should be overwritten with a custom function if wanted. 289 | 290 | :param sprite_map: _spritemap_base - The calling sprite map 291 | :param current_subimage: Int - The current subimage (rounded down to an integer) 292 | :param prev_subimage: Int - The previous subimage (rounded down to an integer) 293 | :return: None 294 | 295 | """ 296 | 297 | pass 298 | 299 | def on_animation_change(self, sprite_map, current_anim, prev_anim): 300 | 301 | """ 302 | Stub callback that runs when the SpriteMap changes animations (i.e. you use play() with a new animation). 303 | Should be overwritten with a custom function if wanted. 304 | 305 | :param sprite_map: _spritemap_base - The calling sprite map 306 | :param current_anim: Str - The name of the current animation 307 | :param prev_anim: Str - The name of the previous animation 308 | :return: None 309 | """ 310 | 311 | pass 312 | 313 | def on_animation_finished(self, sprite_map): 314 | 315 | """ 316 | Stub callback that runs when the SpriteMap finishes its current animation. 317 | Should be overwritten with a custom function if wanted. 318 | 319 | :param sprite_map: _spritemap_base - The calling sprite map 320 | :return: None 321 | """ 322 | 323 | pass 324 | 325 | 326 | class SpriteMapUV(_spritemap_base): 327 | 328 | def __init__(self, obj): 329 | 330 | """ 331 | Creates a UV-based SpriteMap to animate an object by. UV SpriteMap animations take the form of: 332 | [column, row frame 0, row frame 1, row frame 2, etc] 333 | So if you have the sprite already defined in Blender, then 0, 0 would be the bottom left corner of the sprite 334 | sheet with the sprite size defined. If you have an animation of 4 frames in that column with the sprites 335 | ascending upwards, then the animation would be [0, 0, 1, 2, 3]. 336 | 337 | :param obj: KX_GameObject - Sprite object to perform operations on 338 | :return: SpriteMapUV 339 | """ 340 | 341 | super().__init__(obj) 342 | 343 | self.vertices = [] 344 | 345 | for mesh in self.obj.meshes: 346 | 347 | for mat in range(len(mesh.materials)): 348 | 349 | for v in range(mesh.getVertexArrayLength(mat)): 350 | self.vertices.append(mesh.getVertex(mat, v)) # For UV animation 351 | 352 | uvs = [] 353 | 354 | for v in self.vertices: 355 | uv = v.getUV() 356 | 357 | uvs.append([v, uv.x + uv.y]) 358 | 359 | uvs.sort(key=lambda vuv: vuv[1]) 360 | 361 | self.uv_size = [abs(uvs[-1][0].getUV()[0] - uvs[0][0].getUV()[0]), 362 | abs(uvs[-1][0].getUV()[1] - uvs[0][0].getUV()[1])] 363 | 364 | self.offset = {} 365 | 366 | self.flip_x = False 367 | self.flip_y = False 368 | 369 | for vert in self.vertices: 370 | self.offset[vert] = vert.getUV() - uvs[0][0].getUV() # The offset from the bottom-left vertex 371 | 372 | def play(self, anim_name, reset_subimage_on_change=True): 373 | 374 | p = super().play(anim_name, reset_subimage_on_change) 375 | 376 | if p is False: # There was an error; the spritemap probably is trying to play an animation without enough data 377 | return False 378 | 379 | anim = self.anim_dict[self.current_animation]['animation'] 380 | 381 | subi = math.floor(self.subimage) 382 | 383 | for v in range(len(self.vertices)): 384 | 385 | vert = self.vertices[v] 386 | opposite = self.vertices[v - (len(self.vertices) // 2)] # Note this won't work with oddly-numbered, uneven 387 | # meshes; heck, it might not work with subdivided simple planes. Best I can do under the circumstances. 388 | 389 | if self.flip_x: 390 | xcomp = self.offset[opposite].x + (self.uv_size[0] * anim[0]) 391 | else: 392 | xcomp = self.offset[vert].x + (self.uv_size[0] * anim[0]) 393 | 394 | if self.flip_y: 395 | ycomp = self.offset[opposite].y + (self.uv_size[1] * anim[subi]) 396 | else: 397 | ycomp = self.offset[vert].y + (self.uv_size[1] * anim[subi]) 398 | 399 | vert.setUV([xcomp, ycomp]) 400 | 401 | return True 402 | 403 | 404 | class SpriteMapMesh(_spritemap_base): 405 | def __init__(self, obj): 406 | 407 | """ 408 | Creates a mesh-based SpriteMap to animate an object by. Mesh SpriteMap animations take the form of: 409 | [MeshAnimationSeriesName, frame 0, frame 1, frame 2, etc] 410 | 411 | So if you have the sprite meshes already defined in Blender, then the animation would be the prefix of the 412 | sprite mesh names before the frame index, like: 413 | 414 | ["Explosion", 0, 1, 2, 3] 415 | 416 | :param obj: KX_GameObject - Sprite object to perform operations on 417 | :return: SpritemapMesh 418 | """ 419 | 420 | super().__init__(obj) 421 | 422 | def play(self, anim_name, reset_subimage_on_change=True): 423 | 424 | p = super().play(anim_name, reset_subimage_on_change) 425 | 426 | if p is False: 427 | return False 428 | 429 | anim = self.anim_dict[self.current_animation]['animation'] 430 | 431 | subi = math.floor(self.subimage) 432 | 433 | anim_cell = anim[0] + str(anim[subi]) 434 | 435 | if self.obj.meshes[0].name != anim_cell: 436 | self.obj.replaceMesh(anim_cell) 437 | 438 | return True 439 | 440 | -------------------------------------------------------------------------------- /BGE/bghelper/time.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic 4 | 5 | # Time-based mechanics. 6 | 7 | 8 | class TimerBank(): 9 | 10 | def __init__(self): 11 | 12 | self.time = 0.0 13 | 14 | self.timers = {} 15 | 16 | def add(self, timer_name): 17 | 18 | self.timers[timer_name] = Timer() 19 | 20 | def set(self, timer_name, time): 21 | 22 | self.timers[timer_name].set(time) 23 | 24 | def get_time_left(self, timer_name): 25 | 26 | return self.timers[timer_name].get_time_left() 27 | 28 | def time_up(self, timer_name): 29 | 30 | return self.timers[timer_name].time_up() 31 | 32 | def update(self): 33 | 34 | for t in self.timers: 35 | 36 | t.update() 37 | 38 | 39 | class Timer(): 40 | 41 | def __init__(self): 42 | 43 | self.time = 0.0 44 | self.wait_time = 0.0 45 | self.set_time = 0.0 46 | self.on = True 47 | 48 | def set(self, time): 49 | self.set_time = self.time 50 | self.wait_time = time 51 | self.on = True 52 | 53 | def add(self, time): 54 | 55 | #self.set_time += time 56 | self.wait_time += time 57 | 58 | def get_time_left(self): 59 | 60 | if self.on: 61 | return self.wait_time - (self.time - self.set_time) 62 | else: 63 | return 0 64 | 65 | def time_up(self): 66 | 67 | if self.on: 68 | return self.get_time_left() <= 0 69 | else: 70 | return False 71 | 72 | def pause(self): 73 | 74 | self.on = False 75 | 76 | def resume(self): 77 | 78 | self.on = True 79 | 80 | def update(self, frequency=0): 81 | 82 | if self.on: 83 | self.time += (1.0 / logic.getLogicTicRate()) * (frequency + 1) 84 | 85 | 86 | timer_bank = TimerBank() 87 | -------------------------------------------------------------------------------- /BGE/bghelper/trail.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic, types 4 | 5 | 6 | class Trail(): 7 | 8 | def __init__(self, stretch=True, spacing=3, reverse=False, vertex_axis="y", 9 | update_on_moving_only=False, trail_target_obj=None, trail_obj=None): 10 | """ 11 | Creates a Trail object. Trail objects influence the vertices of their mesh to react to a trail owner object's 12 | movements. Think of a light trail, or a cape. When the owner moves or jumps or whatever, the trail reacts 13 | accordingly. 14 | 15 | :param stretch: Bool - If the vertices of the trail should stretch and stay in their position until movement. 16 | Think of the difference between a trail from a light cycle from Tron, and a cape. The trail stretches (it starts 17 | with a "nub", and then gets longer as the cycle drives along). The cape, on the other hand, doesn't 18 | stretch (it's always the same length). 19 | :param spacing: Int - The number of frames between the movement and the vertex update (or the vertex update and 20 | the following vertex update). Higher spacing = more delayed reaction on the trail vertices, which means a more 21 | "rough" trail, and a longer trail when stretch is on. Lower spacing = less of a pronounced reaction on the 22 | trail vertices, and a smoother overall trail. Also, a shorter trail when stretch is on. 23 | :param reverse: Bool - If the trail should reverse it's orientation based on the movement of the trail owner. 24 | E.G. If reverse is on, and a sword swings left, its trail swings left too. 25 | :param vertex_axis: Str - The axis that the vertices of the trail move on, going away from the origin of the 26 | trail object's mesh. Should be "x", "y", or "z". 27 | :param update_on_moving_only: Bool - If the trail should update only when the object's moving. 28 | :param trail_target_obj: KX_GameObject reference - The object that influences the trail. The trail object will 29 | snap to the trail target object's position and orientation. If left to None, this will default to the trail 30 | object's parent. Although, to get the best results, the trail shouldn't be parented to its owner. See the 31 | example blend file. 32 | :param trail_obj: The object to manipulate to achieve a trail. If left to None, will default to the "calling" 33 | object. 34 | :return: Trail 35 | """ 36 | 37 | if not trail_obj: 38 | o = logic.getCurrentController().owner 39 | else: 40 | o = trail_obj 41 | 42 | assert isinstance(o, types.KX_GameObject) 43 | 44 | self.obj = o 45 | 46 | sce = self.obj.scene 47 | 48 | #if trail_mesh is None: 49 | 50 | mesh = self.obj.meshes[0] 51 | 52 | #else: 53 | 54 | # mesh = trail_mesh 55 | 56 | self.trail_axis = vertex_axis 57 | 58 | self.trail_verts = [] 59 | 60 | for mat in range(mesh.numMaterials): 61 | 62 | for v in range(mesh.getVertexArrayLength(mat)): 63 | vert = mesh.getVertex(mat, v) 64 | 65 | self.trail_verts.append(vert) # Get all verts 66 | 67 | self.vert_offsets = {} 68 | 69 | for vert in self.trail_verts: 70 | self.vert_offsets[vert] = vert.XYZ 71 | 72 | vert_pairs = {} 73 | 74 | for vert in self.trail_verts: 75 | 76 | if self.trail_axis.lower() == 'x': 77 | vert_y = round(vert.x,2) # Round it off to ensure that verts that have very close X positions 78 | # (i.e. 0 and 0.01) get grouped together 79 | elif self.trail_axis.lower() == 'y': 80 | vert_y = round(vert.y, 2) 81 | else: 82 | vert_y = round(vert.z, 2) 83 | 84 | if vert_y not in vert_pairs: 85 | vert_pairs[vert_y] = [] 86 | 87 | vert_pairs[vert_y].append(vert) # Get the verts paired with their positions 88 | 89 | self.vert_pairs = [] 90 | 91 | for vp in vert_pairs: 92 | self.vert_pairs.append([vp, vert_pairs[vp]]) 93 | 94 | self.vert_pairs = sorted(self.vert_pairs, key=lambda x: x[0], reverse=True) 95 | 96 | self.target_positions = [] 97 | 98 | self.trail_stretch = stretch # Stretch the trail to 'fit' the movements 99 | 100 | self.trail_spacing = spacing # Number of frames between each edge in the trail 101 | 102 | self.trail_reverse = reverse # If the bending of the trail should be reversed 103 | 104 | self.update_on_moving_only = update_on_moving_only # Update the trail only when moving 105 | 106 | self.trail_counter = spacing # Number of frames between each 'keyframe' 107 | 108 | if not trail_target_obj: 109 | 110 | self.target = self.obj.parent 111 | 112 | else: 113 | 114 | self.target = trail_target_obj 115 | 116 | self.target_past_pos = self.target.worldPosition.copy() 117 | self.target_past_ori = self.target.worldOrientation.copy() 118 | 119 | for x in range(len(self.vert_pairs) * self.trail_spacing): 120 | self.target_positions.insert(0, [self.target.worldPosition.copy(), self.target.worldOrientation.copy()]) 121 | 122 | def update(self): 123 | 124 | """ 125 | Update the trail. 126 | :return: 127 | """ 128 | 129 | if not self.obj.parent == self.target: 130 | self.obj.worldPosition = self.target.worldPosition 131 | self.obj.worldOrientation = self.target.worldOrientation 132 | 133 | target_info = [self.target.worldPosition.copy(), self.target.worldOrientation.copy()] 134 | 135 | insert = 0 136 | 137 | if not self.update_on_moving_only: 138 | insert = 1 139 | else: 140 | 141 | pos_diff = (self.target.worldPosition - self.target_past_pos).magnitude 142 | ori_diff = (self.target.worldOrientation - self.target_past_ori).median_scale 143 | threshold = 0.0001 144 | 145 | if pos_diff > threshold or ori_diff > threshold: 146 | insert = 1 147 | 148 | if insert: 149 | self.target_positions.insert(0, target_info) 150 | 151 | if len(self.target_positions) > len(self.vert_pairs) * self.trail_spacing: 152 | self.target_positions.pop() # Remove oldest position value 153 | 154 | for vp in range(0, len(self.vert_pairs)): 155 | 156 | verts = self.vert_pairs[vp][1] 157 | 158 | if len(self.target_positions) > vp * self.trail_spacing: 159 | 160 | pos = self.target_positions[vp * self.trail_spacing][0] 161 | ori = self.target_positions[vp * self.trail_spacing][1] 162 | 163 | for vert in verts: 164 | 165 | if not self.trail_reverse: 166 | 167 | if self.trail_stretch: # Factor in position of the target to 'stretch' (useful for trails, 168 | # where the end point stays still, hopefully, until the rest 'catches up'') 169 | diff = (pos - self.target.worldPosition) * ori 170 | vert.XYZ = (self.vert_offsets[vert] + diff) * self.target.worldOrientation 171 | else: # Don't factor in movement of the trail 172 | # (useful for things that wouldn't stretch, like scarves) 173 | vert.XYZ = self.vert_offsets[vert] * self.target.worldOrientation 174 | vert.XYZ = vert.XYZ * ori.inverted() 175 | 176 | else: # Reverse the trail's direction 177 | 178 | if self.trail_stretch: 179 | diff = (pos - self.target.worldPosition) * ori # .inverted() 180 | vert.XYZ = (self.vert_offsets[vert] + diff) * self.target.worldOrientation.inverted() 181 | else: 182 | vert.XYZ = self.vert_offsets[vert] * self.target.worldOrientation.inverted() 183 | vert.XYZ = vert.XYZ * ori 184 | 185 | self.target_past_pos = self.target.worldPosition.copy() 186 | self.target_past_ori = self.target.worldOrientation.copy() 187 | -------------------------------------------------------------------------------- /BGE/bghelper/viewport.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | import collections 4 | 5 | import bge 6 | 7 | 8 | class CViewManager(): 9 | """ 10 | A static class that you don't have to instantiate to use. 11 | """ 12 | 13 | AH_NONE = 0 14 | 15 | AH_VERTICAL = 1 # Increases from first camera added at top to last camera added at bottom 16 | AH_VERTICAL_REVERSED = 2 # Goes the other way 17 | 18 | AH_HORIZONTAL = 3 # Increases from first camera added at left to last camera added at right 19 | AH_HORIZONTAL_REVERSED = 4 # Goes... 20 | 21 | def __init__(self): 22 | 23 | self.views = collections.OrderedDict() # Views and their setups. 24 | 25 | self.remove_all_views() # Initialize view dictionary 26 | 27 | def add_view(self, name, auto_set_hint=AH_NONE): 28 | 29 | self.views[name] = {'camera': None, 30 | 'w': 0.0, 31 | 'h': 0.0, 32 | 'x': 0.0, 33 | 'y': 0.0, 34 | } 35 | 36 | if auto_set_hint != self.AH_NONE: 37 | 38 | num_of_views = len(self.views) 39 | 40 | for v in self.views: 41 | 42 | view = self.views[v] 43 | v_index = list(self.views.keys()).index(v) 44 | 45 | if auto_set_hint == self.AH_HORIZONTAL: 46 | 47 | self.set_view_position(v, ((1.0 / num_of_views) * v_index), 0.0) 48 | self.set_view_size(v, 1.0 / num_of_views, 1.0) 49 | 50 | elif auto_set_hint == self.AH_HORIZONTAL_REVERSED: 51 | 52 | self.set_view_position(v, 1.0 - ((1.0 / num_of_views) * (v_index + 1)), 0.0) 53 | self.set_view_size(v, 1.0 / num_of_views, 1.0) 54 | 55 | elif auto_set_hint == self.AH_VERTICAL: 56 | 57 | self.set_view_position(v, 0.0, 1.0 - ((1.0 / num_of_views) * (v_index + 1))) 58 | self.set_view_size(v, 1.0, 1.0 / num_of_views) 59 | 60 | elif auto_set_hint == self.AH_VERTICAL_REVERSED: 61 | 62 | self.set_view_position(v, 0.0, (1.0 / num_of_views) * v_index) 63 | self.set_view_size(v, 1.0, 1.0 / num_of_views) 64 | 65 | pass # Reconfigure the views to orient correctly 66 | 67 | self.update_views() 68 | 69 | def remove_view(self, name): 70 | 71 | del self.views[name] 72 | self.update_views() 73 | 74 | def remove_all_views(self): 75 | 76 | self.views.clear() # Clear views to ensure that you're not dealing with any 77 | self.add_view('default') # Add a default view (the current one) 78 | self.set_view_size('default', 1.0, 1.0) # View size is set in fractions for ease of use 79 | self.set_camera('default', bge.logic.getCurrentScene().active_camera) # Set the camera to be the active one 80 | 81 | self.update_views() 82 | 83 | def set_camera(self, name, camera): 84 | 85 | self.views[name]['camera'] = camera 86 | self.update_views() 87 | 88 | def set_view_size(self, name, width, height): 89 | 90 | self.views[name]['w'] = width 91 | self.views[name]['h'] = height 92 | self.update_views() 93 | 94 | def set_view_position(self, name, pos_x, pos_y): 95 | 96 | self.views[name]['x'] = pos_x 97 | self.views[name]['y'] = pos_y 98 | self.update_views() 99 | 100 | def update_views(self): 101 | 102 | for v in self.views: 103 | 104 | view = self.views[v] 105 | 106 | view_cam = view['camera'] 107 | 108 | if view_cam is not None and not view_cam.invalid: 109 | 110 | assert isinstance(view_cam, bge.types.KX_Camera) 111 | 112 | win_w = bge.render.getWindowWidth() 113 | win_h = bge.render.getWindowHeight() 114 | 115 | view_cam.setViewport(int(view['x'] * win_w), int(view['y'] * win_h), 116 | int(view['x'] * win_w) + int(view['w'] * win_w), 117 | int(view['y'] * win_h) + int(view['h'] * win_h)) 118 | 119 | if len(self.views) > 1: # You're not just using one view 120 | 121 | view_cam.useViewport = True 122 | 123 | else: 124 | 125 | view_cam.useViewport = False # Gotta test this out 126 | 127 | else: 128 | 129 | if view_cam is not None and view_cam.invalid: 130 | print('ERROR: Camera for view ' + v + ' has become invalid.') 131 | 132 | view['camera'] = None 133 | 134 | 135 | manager = CViewManager() -------------------------------------------------------------------------------- /BGE/bghelper/window.py: -------------------------------------------------------------------------------- 1 | __author__ = 'SolarLune' 2 | 3 | from bge import logic, render, events 4 | 5 | 6 | def get_aspect_ratio(width_first=True): 7 | """ 8 | Returns the aspect ratio of the game window, or the window's width / the window's height. 9 | If width_first is True (default), then it will return the window's height / the window's width. 10 | :param width_first: Bool - Whether to divide the height by the width. 11 | :return: Float - The aspect ratio of the window 12 | """ 13 | 14 | if width_first: 15 | 16 | return render.getWindowWidth() / render.getWindowHeight() 17 | 18 | else: 19 | 20 | return render.getWindowHeight() / render.getWindowWidth() 21 | 22 | 23 | def maintain_asr(ratio: float, width=None): 24 | """ 25 | Sets the window's size to be the width specified and the width divided by the desired aspect ratio as the height. 26 | You can use this to ensure you maintain a 16:9 view, or 4:3, or whatever ratio you desire. 27 | :param width:Int - Desired width of the window. If left to None, is left to the current window width. 28 | :param ratio:Float - Desired aspect ratio to maintain. 29 | :return:None 30 | """ 31 | 32 | if width is None: 33 | width = render.getWindowWidth() 34 | 35 | render.setWindowSize(int(width), int(width / ratio)) 36 | 37 | 38 | def fullscreen_on_keypress(fs_key=events.F4KEY, *modifier_keys): 39 | """ 40 | Causes full-screen on keypress of fs_key. Also checks to see if modifier keys are being held down. 41 | :param fs_key:Int - Keyboard key constant (e.g. events.F4KEY). 42 | :param modifier_keys:Int - Other keys that might need to be held down while the fs_key is pushed. 43 | :return: Bool or None - The new fullscreen state if triggered (i.e. True or False). Otherwise, None. 44 | """ 45 | 46 | if logic.keyboard.events[fs_key] == logic.KX_INPUT_JUST_ACTIVATED: 47 | 48 | all_held = True 49 | 50 | for m in modifier_keys: 51 | 52 | if not logic.keyboard.events[m] == logic.KX_INPUT_ACTIVE: 53 | all_held = False 54 | 55 | if all_held: 56 | new_fs = not render.getFullScreen() 57 | 58 | render.setFullScreen(new_fs) 59 | 60 | return new_fs 61 | 62 | return None -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 SolarLune 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo is now archived; please see [my other individual created repos for game development](https://github.com/SolarLune). 2 | --------------------------------------------------------------------------------