connecting to character create menu doesn't crash

This commit is contained in:
austin 2022-05-02 21:43:04 -04:00
parent ad556e3553
commit 5b4f3cede7
13 changed files with 531 additions and 74 deletions

File diff suppressed because one or more lines are too long

View File

@ -40,6 +40,7 @@ import electrosphere.game.simulation.MicroSimulation;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals; import electrosphere.main.Globals;
import electrosphere.menu.MenuGenerators; import electrosphere.menu.MenuGenerators;
import electrosphere.menu.MenuGeneratorsMultiplayer;
import electrosphere.menu.WindowStrings; import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils; import electrosphere.menu.WindowUtils;
import electrosphere.net.NetUtils; import electrosphere.net.NetUtils;
@ -64,6 +65,7 @@ import electrosphere.game.server.unit.UnitUtils;
import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.WidgetUtils; import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.Window; import electrosphere.renderer.ui.Window;
import electrosphere.util.Utilities;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -161,7 +163,7 @@ public class LoadingThread extends Thread {
//set simulations to ready if they exist //set simulations to ready if they exist
setSimulationsToReady(); setSimulationsToReady();
//log //log
LoggerInterface.loggerEngine.INFO("[Server]Finished loading"); LoggerInterface.loggerEngine.INFO("[Server]Finished loading main world");
break; break;
@ -196,7 +198,7 @@ public class LoadingThread extends Thread {
creatingRandomEntities(); creatingRandomEntities();
//set simulations to ready if they exist //set simulations to ready if they exist
setSimulationsToReady(); setSimulationsToReady();
LoggerInterface.loggerEngine.INFO("[Server]Finished loading"); LoggerInterface.loggerEngine.INFO("[Server]Finished loading arena world");
break; break;
case LOAD_CHARACTER_SERVER: case LOAD_CHARACTER_SERVER:
@ -207,25 +209,26 @@ public class LoadingThread extends Thread {
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT);
//initialize the client thread (client) //initialize the client thread (client)
initClientThread(); initClientThread();
//while we don't know what races are playable, wait
while(Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces().size() == 0){
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException ex) {}
}
//once we have them, bring up the character creation interface
//init character creation window //init character creation window
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); //eventually should replace with at ui to select an already created character or create a new one
WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow());
//make loading dialog disappear //make loading dialog disappear
loadingWindow.setVisible(false); loadingWindow.setVisible(false);
//make character creation window visible //make character creation window visible
WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true); WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true);
//recapture window //recapture window
Globals.controlHandler.setShouldRecapture(true); Globals.controlHandler.setShouldRecapture(true);
//set rendering flags
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = true;
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = true;
Globals.RENDER_FLAG_RENDER_UI = true;
Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false;
Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false;
//log //log
LoggerInterface.loggerEngine.INFO("[Client]Finished loading"); LoggerInterface.loggerEngine.INFO("[Client]Finished loading character creation menu");
//set controls state //set menu controls again
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME); Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.TITLE_MENU);
break; break;
case LOAD_CLIENT_WORLD: case LOAD_CLIENT_WORLD:
@ -265,7 +268,7 @@ public class LoadingThread extends Thread {
Globals.RENDER_FLAG_RENDER_UI = true; Globals.RENDER_FLAG_RENDER_UI = true;
Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false; Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false;
Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false;
LoggerInterface.loggerEngine.INFO("[Client]Finished loading"); LoggerInterface.loggerEngine.INFO("[Client]Finished loading main game");
//set controls state //set controls state
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME); Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME);
break; break;

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Semaphore;
public class CreatureTypeLoader { public class CreatureTypeLoader {
@ -11,6 +12,8 @@ public class CreatureTypeLoader {
List<String> playableRaceNames = new LinkedList<String>(); List<String> playableRaceNames = new LinkedList<String>();
Semaphore playableRaceLock = new Semaphore(1);
public void putCreature(String name, CreatureType type){ public void putCreature(String name, CreatureType type){
creatureMap.put(name,type); creatureMap.put(name,type);
} }
@ -24,7 +27,21 @@ public class CreatureTypeLoader {
} }
public List<String> getPlayableRaces(){ public List<String> getPlayableRaces(){
return playableRaceNames; List<String> races = null;
playableRaceLock.acquireUninterruptibly();
races = playableRaceNames;
playableRaceLock.release();
return races;
}
public void clearPlayableRaces(){
playableRaceNames.clear();
}
public void loadPlayableRaces(List<String> races){
playableRaceLock.acquireUninterruptibly();
playableRaceNames = races;
playableRaceLock.release();
} }

View File

@ -33,6 +33,7 @@ import electrosphere.renderer.ui.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.DraggableElement.DragEventCallback; import electrosphere.renderer.ui.DraggableElement.DragEventCallback;
import electrosphere.renderer.ui.FocusableElement.FocusEventCallback; import electrosphere.renderer.ui.FocusableElement.FocusEventCallback;
import electrosphere.renderer.ui.NavigableElement.NavigationEventCallback; import electrosphere.renderer.ui.NavigableElement.NavigationEventCallback;
import electrosphere.renderer.ui.ValueElement.ValueChangeEventCallback;
import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div; import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel; import electrosphere.renderer.ui.elements.ImagePanel;
@ -44,6 +45,7 @@ import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.DragEvent;
import electrosphere.renderer.ui.events.FocusEvent; import electrosphere.renderer.ui.events.FocusEvent;
import electrosphere.renderer.ui.events.NavigationEvent; import electrosphere.renderer.ui.events.NavigationEvent;
import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.form.FormElement; import electrosphere.renderer.ui.form.FormElement;
import java.util.List; import java.util.List;
@ -100,7 +102,7 @@ public class MenuGenerators {
arenaButton.addChild(arenaLabel); arenaButton.addChild(arenaLabel);
rVal.addChild(arenaButton); rVal.addChild(arenaButton);
arenaButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ arenaButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CLIENT_WORLD); LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER);
Globals.loadingThreadsList.add(clientThread); Globals.loadingThreadsList.add(clientThread);
LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_ARENA); LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_ARENA);
Globals.loadingThreadsList.add(serverThread); Globals.loadingThreadsList.add(serverThread);
@ -778,7 +780,8 @@ public class MenuGenerators {
int posY = offset * 350 + 100; int posY = offset * 350 + 100;
if(attribute.getType().equals("bone")){ if(attribute.getType().equals("bone")){
Slider attributeSlider = new Slider(50,posY,400,100,new Vector3f(0.1f,0.1f,0.1f),new Vector3f(1.0f,0,0)); Slider attributeSlider = new Slider(50,posY,400,100,new Vector3f(0.1f,0.1f,0.1f),new Vector3f(1.0f,0,0));
attributeSlider.setOnValueChange(new Slider.OnSliderChangeCallback() {public void onChange(float value) { attributeSlider.setOnValueChangeCallback(new ValueChangeEventCallback() {public void execute(ValueChangeEvent event) {
float value = event.getAsFloat();
float minVal = attribute.getMinValue(); float minVal = attribute.getMinValue();
float range = attribute.getMaxValue() - minVal; float range = attribute.getMaxValue() - minVal;
float actualValue = minVal + range * value; float actualValue = minVal + range * value;

View File

@ -1,51 +1,52 @@
package electrosphere.menu; package electrosphere.menu;
import electrosphere.main.Globals;
import electrosphere.renderer.ui.ClickableElement;
import electrosphere.renderer.ui.Element; import electrosphere.renderer.ui.Element;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.StringCarousel;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.form.FormElement; import electrosphere.renderer.ui.form.FormElement;
public class MenuGeneratorsMultiplayer { public class MenuGeneratorsMultiplayer {
public static Element createMultiplayerCharacterSelectionWindow(){
FormElement rVal = new FormElement();
// int screenTop = Globals.WINDOW_HEIGHT - 150;
int verticalPosition = 125;
//button (create)
Button createButton = new Button();
Label createLabel = new Label(100,125 + verticalPosition + 200,1.0f);
createLabel.setText("Create Character");
createButton.addChild(createLabel);
rVal.addChild(createButton);
createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldCreationMenu());
return false;
}});
return rVal;
}
public static Element createMultiplayerCharacterCreationWindow(){ public static Element createMultiplayerCharacterCreationWindow(){
FormElement rVal = new FormElement(); FormElement rVal = new FormElement();
// int screenTop = Globals.WINDOW_HEIGHT - 150; // int screenTop = Globals.WINDOW_HEIGHT - 150;
List<String> saveNames = SaveUtils.getSaves();
int verticalPosition = 125; int verticalPosition = 125;
for(String saveName : saveNames){
if(!saveName.startsWith(".")){
//button (select save) //select race
Button selectButton = new Button(); StringCarousel raceCarousel = new StringCarousel(100, 125, 1.0f);
Label selectLabel = new Label(100,125 + verticalPosition,1.0f); for(String raceName : Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces()){
selectLabel.setText(saveName.toUpperCase()); raceCarousel.addOption(raceName);
selectButton.addChild(selectLabel);
rVal.addChild(selectButton);
selectButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
if(SaveUtils.worldHasSave(saveName.toLowerCase())){
LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER);
Globals.loadingThreadsList.add(clientThread);
LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_MAIN_GAME);
Globals.loadingThreadsList.add(serverThread);
Globals.RUN_CLIENT = true;
Globals.RUN_SERVER = true;
clientThread.start();
serverThread.start();
} else {
Globals.currentSaveName = saveName.toLowerCase();
SaveUtils.loadTerrainAndCreateWorldData();
WindowUtils.replaceMainMenuContents(MenuGenerators.createSaveCreationMenu());
}
return false;
}});
verticalPosition = verticalPosition + 75;
}
} }
rVal.addChild(raceCarousel);
//button (create) //button (create)
Button createButton = new Button(); Button createButton = new Button();
Label createLabel = new Label(100,125 + verticalPosition + 200,1.0f); Label createLabel = new Label(100,275,1.0f);
createLabel.setText("Create World"); createLabel.setText("Create Character");
createButton.addChild(createLabel); createButton.addChild(createLabel);
rVal.addChild(createButton); rVal.addChild(createButton);
createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){

View File

@ -22,6 +22,7 @@ import java.util.Properties;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -44,14 +45,30 @@ public class ClientNetworking implements Runnable{
NetworkParser parser; NetworkParser parser;
ClientProtocol clientProtocol = new ClientProtocol(); ClientProtocol clientProtocol = new ClientProtocol();
static final int MAX_CONNECTION_ATTEMPTS = 10;
public ClientNetworking(String address, int port){ public ClientNetworking(String address, int port){
try { int connectionAttempts = 0;
this.socket = new Socket(address,port); boolean connected = false;
} catch (IOException ex) { while(!connected){
System.err.println("Failed to connect!"); try {
ex.printStackTrace(); this.socket = new Socket(address,port);
System.exit(1); } catch (IOException ex) {
LoggerInterface.loggerNetworking.WARNING("Client failed to connect!");
} finally {
connected = true;
}
if(!connected){
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {}
connectionAttempts++;
}
if(connectionAttempts > MAX_CONNECTION_ATTEMPTS){
LoggerInterface.loggerNetworking.ERROR("Max client connection attempts!", new Exception());
System.exit(1);
}
} }
} }

View File

@ -4,6 +4,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals; import electrosphere.main.Globals;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.LoreMessage;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
public class AuthProtocol { public class AuthProtocol {
@ -17,8 +18,10 @@ public class AuthProtocol {
break; break;
case AUTHSUCCESS: case AUTHSUCCESS:
//trigger request to spawn character //trigger request to spawn character
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage()); // Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); // Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());
//request playable races
Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage());
break; break;
case AUTHFAILURE: case AUTHFAILURE:
//TODO: handle better //TODO: handle better

View File

@ -11,6 +11,7 @@ import electrosphere.main.Globals;
import electrosphere.main.Main; import electrosphere.main.Main;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.LoreMessage;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.message.ServerMessage;
@ -41,6 +42,9 @@ public class ClientProtocol {
case SERVER_MESSAGE: case SERVER_MESSAGE:
ServerProtocol.handleServerMessage((ServerMessage)message); ServerProtocol.handleServerMessage((ServerMessage)message);
break; break;
case LORE_MESSAGE:
LoreProtocol.handleLoreMessage((LoreMessage)message);
break;
} }
} }

View File

@ -0,0 +1,32 @@
package electrosphere.net.client.protocol;
import java.util.List;
import com.google.gson.Gson;
import electrosphere.main.Globals;
import electrosphere.net.parser.net.message.LoreMessage;
public class LoreProtocol {
protected static void handleLoreMessage(LoreMessage message){
switch(message.getMessageSubtype()){
case RESPONSEDATA:
break;
case RESPONSERACES:
//we get back the race list as a json array, deserialize, and push into type loader
List<String> playableRaces = new Gson().fromJson(message.getdata(), List.class);
Globals.gameConfigCurrent.getCreatureTypeLoader().loadPlayableRaces(playableRaces);
break;
case RESPONSERACEDATA:
break;
case REQUESTDATA:
case REQUESTRACEDATA:
case REQUESTRACES:
//silently ignore
break;
}
}
}

View File

@ -0,0 +1,19 @@
package electrosphere.renderer.ui;
import electrosphere.renderer.ui.events.ValueChangeEvent;
/**
* Describes an element that contains a changeable value which will be used by the program somewhere
* IE a carousel, text input, radio dial, etc
*/
public interface ValueElement extends Element {
public void setOnValueChangeCallback(ValueChangeEventCallback callback);
public interface ValueChangeEventCallback {
public void execute(ValueChangeEvent event);
}
}

View File

@ -11,6 +11,7 @@ import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.FocusableElement; import electrosphere.renderer.ui.FocusableElement;
import electrosphere.renderer.ui.KeyEventElement; import electrosphere.renderer.ui.KeyEventElement;
import electrosphere.renderer.ui.MenuEventElement; import electrosphere.renderer.ui.MenuEventElement;
import electrosphere.renderer.ui.ValueElement;
import electrosphere.renderer.ui.events.ClickEvent; import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.DragEvent;
import electrosphere.renderer.ui.events.Event; import electrosphere.renderer.ui.events.Event;
@ -18,9 +19,10 @@ import electrosphere.renderer.ui.events.FocusEvent;
import electrosphere.renderer.ui.events.KeyboardEvent; import electrosphere.renderer.ui.events.KeyboardEvent;
import electrosphere.renderer.ui.events.MenuEvent; import electrosphere.renderer.ui.events.MenuEvent;
import electrosphere.renderer.ui.events.MouseEvent; import electrosphere.renderer.ui.events.MouseEvent;
import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.events.DragEvent.DragEventType; import electrosphere.renderer.ui.events.DragEvent.DragEventType;
public class Slider implements ClickableElement, DraggableElement, FocusableElement, DrawableElement, MenuEventElement { public class Slider implements ClickableElement, DraggableElement, FocusableElement, DrawableElement, MenuEventElement, ValueElement {
public int width = 1; public int width = 1;
public int height = 1; public int height = 1;
@ -41,7 +43,7 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
DragEventCallback onDragRelease; DragEventCallback onDragRelease;
ClickEventCallback onClick; ClickEventCallback onClick;
MenuEventCallback onMenuEvent; MenuEventCallback onMenuEvent;
OnSliderChangeCallback onChangeValue; ValueChangeEventCallback onValueChange;
float min = 0.0f; float min = 0.0f;
float max = 1.0f; float max = 1.0f;
@ -238,8 +240,9 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
onMenuEvent = callback; onMenuEvent = callback;
} }
public void setOnValueChange(OnSliderChangeCallback callback){ @Override
this.onChangeValue = callback; public void setOnValueChangeCallback(ValueChangeEventCallback callback) {
onValueChange = callback;
} }
@ -272,15 +275,15 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
switch(menuEvent.getType()){ switch(menuEvent.getType()){
case INCREMENT: case INCREMENT:
value = Math.min(value + ((max - min) * 0.01f),max); value = Math.min(value + ((max - min) * 0.01f),max);
if(onChangeValue != null){ if(onValueChange != null){
onChangeValue.onChange(value); onValueChange.execute(new ValueChangeEvent(value));
} }
propagate = false; propagate = false;
break; break;
case DECREMENT: case DECREMENT:
value = Math.max(value - ((max - min) * 0.01f),min); value = Math.max(value - ((max - min) * 0.01f),min);
if(onChangeValue != null){ if(onValueChange != null){
onChangeValue.onChange(value); onValueChange.execute(new ValueChangeEvent(value));
} }
propagate = false; propagate = false;
break; break;
@ -320,8 +323,8 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
int percentage = clickEvent.getCurrentX() - positionX; int percentage = clickEvent.getCurrentX() - positionX;
int max = width; int max = width;
value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f);
if(onChangeValue != null){ if(onValueChange != null){
onChangeValue.onChange(value); onValueChange.execute(new ValueChangeEvent(value));
} }
propagate = false; propagate = false;
} }
@ -329,12 +332,5 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
} }
return propagate; return propagate;
} }
public interface OnSliderChangeCallback {
public void onChange(float value);
}
} }

View File

@ -0,0 +1,326 @@
package electrosphere.renderer.ui.elements;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3f;
import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.Element;
import electrosphere.renderer.ui.FocusableElement;
import electrosphere.renderer.ui.MenuEventElement;
import electrosphere.renderer.ui.ValueElement;
import electrosphere.renderer.ui.events.Event;
import electrosphere.renderer.ui.events.FocusEvent;
import electrosphere.renderer.ui.events.MenuEvent;
import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.events.MenuEvent.MenuEventType;
import electrosphere.renderer.ui.font.FontUtils;
import electrosphere.renderer.ui.font.bitmapchar.BitmapCharacter;
public class StringCarousel implements DrawableElement, MenuEventElement, FocusableElement, ValueElement {
public int width = 1;
public int height = 1;
public int positionX = 0;
public int positionY = 0;
public int parentWidth = 1;
public int parentHeight = 1;
public boolean visible = false;
MenuEventCallback onMenuEventCallback;
ValueChangeEventCallback onValueChange;
boolean focused = false;
FocusEventCallback onFocusCallback;
FocusEventCallback onLoseFocusCallback;
List<String> options = new LinkedList<String>();
int currentOption = -1;
String textCurrent = "";
int textPixelWidth = 0;
float fontSize = 1.0f;
List<BitmapCharacter> childrenElements = new LinkedList<BitmapCharacter>();
public StringCarousel(int x, int y, float fontSize){
this.positionX = x;
this.positionY = y;
this.width = 0;
this.height = (int)(FontUtils.getFontHeight() * fontSize);
this.fontSize = fontSize;
}
public void addOption(String option){
options.add(option);
if(currentOption == -1){
currentOption = 0;
setText(option);
if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(option));
}
}
}
public List<String> getOptions(){
return options;
}
public void removeOption(String option){
options.remove(option);
if(currentOption > options.size() - 1){
currentOption = options.size() - 1;
}
}
void generateLetters(){
childrenElements.clear();
int rollingOffset = 0;
for(int i = 0; i < textCurrent.length(); i++){
char toDraw = textCurrent.charAt(i);
Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(toDraw);
BitmapCharacter newLetter = new BitmapCharacter((int)(rollingOffset * fontSize) + positionX, positionY, (int)(bitMapDimension.x * fontSize), this.height, toDraw);
rollingOffset += (int)bitMapDimension.x;
childrenElements.add(newLetter);
}
}
public void setText(String text){
this.textCurrent = text;
textPixelWidth = 0;
for(int i = 0; i < text.length(); i++){
Vector3f bitMapDimension = FontUtils.getDimensionOfCharacterDiscrete(text.charAt(i));
textPixelWidth = textPixelWidth + (int)bitMapDimension.x;
}
generateLetters();
if(focused){
setColor(new Vector3f(1,0,0));
}
}
public void setColor(Vector3f color){
for(BitmapCharacter character : childrenElements){
character.setColor(color);
}
}
public String getText(){
return textCurrent;
}
@Override
public void draw(int parentFramebufferPointer, int parentWidth, int parentHeight) {
for(DrawableElement child : childrenElements){
child.draw(parentFramebufferPointer, parentWidth, parentHeight);
}
}
public int getWidth() {
int minX = -1;
int maxX = -1;
for(BitmapCharacter child : childrenElements){
if(minX == -1){
minX = child.getPositionX();
} else if(child.getPositionX() < minX){
minX = child.getPositionX();
}
if(maxX == -1){
maxX = child.getPositionX() + child.getWidth();
} else if(child.getPositionX() + child.getWidth() > maxX){
maxX = child.getPositionX() + child.getWidth();
}
}
if(minX == -1){
minX = 0;
}
if(maxX == -1){
maxX = 0;
}
return maxX - minX;
}
public int getHeight() {
int minY = -1;
int maxY = -1;
for(BitmapCharacter child : childrenElements){
if(minY == -1){
minY = child.getPositionY();
} else if(child.getPositionY() < minY){
minY = child.getPositionY();
}
if(maxY == -1){
maxY = child.getPositionY() + child.getHeight();
} else if(child.getPositionY() + child.getHeight() > maxY){
maxY = child.getPositionY() + child.getHeight();
}
}
if(minY == -1){
minY = 0;
}
if(maxY == -1){
maxY = 0;
}
return maxY - minY;
}
public int getPositionX() {
int minX = -1;
for(BitmapCharacter child : childrenElements){
if(minX == -1){
minX = child.getPositionX();
} else if(child.getPositionX() < minX){
minX = child.getPositionX();
}
}
if(minX == -1){
minX = 0;
}
return minX;
}
public int getPositionY() {
int minY = -1;
for(BitmapCharacter child : childrenElements){
if(minY == -1){
minY = child.getPositionY();
} else if(child.getPositionY() < minY){
minY = child.getPositionY();
}
}
if(minY == -1){
minY = 0;
}
return minY;
}
public boolean getVisible() {
return visible;
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public void setPositionX(int posX) {
int deltaX = posX - this.positionX;
this.positionX = posX;
for(Element child : childrenElements){
child.setPositionX(child.getPositionX() + deltaX);
}
}
public void setPositionY(int posY) {
int deltaY = posY - this.positionY;
this.positionY = posY;
for(Element child : childrenElements){
child.setPositionY(child.getPositionY() + deltaY);
}
}
public void setVisible(boolean draw) {
this.visible = draw;
}
public void setParentWidth(int width){
parentWidth = width;
}
public void setParentHeight(int height){
this.parentHeight = height;
}
public boolean handleEvent(Event event){
boolean propagate = true;
if(event instanceof MenuEvent){
MenuEvent menuEvent = (MenuEvent)event;
if(onMenuEventCallback != null){
propagate = onMenuEventCallback.execute(menuEvent);
} else {
//default behavior
if(menuEvent.getType() == MenuEventType.INCREMENT){
propagate = false;
if(options.size() > 0){
currentOption++;
if(currentOption > options.size() - 1){
currentOption = 0;
}
String newOption = options.get(currentOption);
setText(newOption);
if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(newOption));
}
}
} else if(menuEvent.getType() == MenuEventType.DECREMENT){
propagate = false;
if(options.size() > 0){
currentOption--;
if(currentOption < 0){
currentOption = options.size() - 1;
}
String newOption = options.get(currentOption);
setText(newOption);
if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(newOption));
}
}
}
}
} else if(event instanceof FocusEvent){
FocusEvent focusEvent = (FocusEvent) event;
if(focusEvent.isFocused()){
this.focused = true;
if(onFocusCallback != null){
propagate = onFocusCallback.execute(focusEvent);
} else {
//default behavior
propagate = false;
setColor(new Vector3f(1,0,0));
}
} else {
this.focused = false;
if(onLoseFocusCallback != null){
propagate = onLoseFocusCallback.execute(focusEvent);
} else {
//default behavior
propagate = false;
setColor(new Vector3f(1,1,1));
}
}
}
return propagate;
}
@Override
public void setOnMenuEventCallback(MenuEventCallback callback) {
onMenuEventCallback = callback;
}
@Override
public void setOnValueChangeCallback(ValueChangeEventCallback callback) {
onValueChange = callback;
}
@Override
public boolean isFocused() {
return focused;
}
@Override
public void setOnFocus(FocusEventCallback callback) {
onFocusCallback = callback;
}
@Override
public void setOnLoseFocus(FocusEventCallback callback) {
onLoseFocusCallback = callback;
}
}

View File

@ -0,0 +1,36 @@
package electrosphere.renderer.ui.events;
public class ValueChangeEvent {
public static enum ValueType {
STRING,
FLOAT,
}
String valueString;
float valueFloat;
ValueType valueType;
public ValueChangeEvent(String value){
valueString = value;
valueType = ValueType.STRING;
}
public ValueChangeEvent(float value){
valueFloat = value;
valueType = ValueType.FLOAT;
}
public ValueType getType(){
return valueType;
}
public float getAsFloat(){
return valueFloat;
}
public String getAsString(){
return valueString;
}
}