diff --git a/docs/NetworkFlow.drawio b/docs/NetworkFlow.drawio
index aed00645..06959372 100644
--- a/docs/NetworkFlow.drawio
+++ b/docs/NetworkFlow.drawio
@@ -1 +1 @@
-7Zhdb5swFIZ/DZeRgmkIvUxY21Xa1KhR195NFpyAN4OZMYP0188uJgnBkRYtJJmSK+z3GH+c5w06seX4SfXAcRZ/ZSFQCw3DynI+WQjZzmgoH0pZ1oo39moh4iTUg9bCnLyDFvV7UUFCyFsDBWNUkKwtBixNIRAtDXPOyvawBaPtVTMcQUeYB5h21VcSilifojmW0j8DieJmZXuoIwluBmshj3HIyg3JubMcnzMm6lZS+UBV8pq81O/d74iuNsYhFX/zQjVB5M2l7jeo4vfnl/LN42igZ/mNaaEPrDcrlk0GOCvSENQkQ8uZljERMM9woKKlZC61WCRU9mzZ7G6qWQG4gGpD0pt8AJaA4Es5REdvdL6W7W65zv7I01q8kfnxWItYE49WM6+TIhs6L3vkCBly5FK57HTB5Ck3k+X+KlgTGOQfZp7IAbaXVeugbEXqOYtxDiraTCd3V89YxzskZAJFO9254Own+IwyLpWUpaAWJ5RuSZiSKJXdQGIBqU8VDiJdPtGBhIShWsbIt+2AAyAetxHbdpex7RoYO30hdjqIGzjd38N5UFBG0V9L2+uFiuOcmsrNTirOxVIZoVNTGe2icilEbLSFpEvkqEDcDpC5wKLIL4fIyD4vIuN/rxiQqWKYFPI4soilso5UJW+SURBAVSZwIZgsOWWWj1ZMUFiIs7PCdmlhKB9tU/mI+vKC15MXvjAOVy/s5QW364XV/8yjeOG2Jy/4MeY4UF/mqyH2MYR3akM0ix3cEc+QZ7hMr37Yxw9oeEQ/vAyK+eTx9vsPfxZW0/yJl0/3huuY18fZf1DHoX7qONPFQF+FnJFH9+pnRnGaqh/OpTIxXQsclYnhrubSmZguBQ7ERHbXV9QfsY2LfufuDw==7Vpdb5swFP01kbaHRHwEmj62Sb+kteqWStueKgsccGowNaZJ9ut3nZgkxO60aQESpS8tHJuLfc652BfScYfJ/IajLL5nIaYdxwrnHXfUcRzb9Sz4J5HFChmcDVZAxEmoOm2AMfmFFaiuiwoS4rzSUTBGBcmqYMDSFAeigiHO2azabcJo9a4ZirAGjANEdfQ7CUWsZlFOS+K3mERxeWfbUi0JKjsrII9RyGZbkHvVcYecMbE6SuZDTCV5JS+r667faV0PjONU/M0FVvfG/no/+fK8SCfRy+v08tl56DqrKG+IFmrCarBiUTLAWZGGWAaxOu7lLCYCjzMUyNYZaA5YLBIKZzYcTlgqlIg2zPBSH6Qa9xvmAs+3IDXoG8wSLPgCuqjWvuJvUT2dbdRwBgqLt5RYd0TKAdE68oYkOFA8/QNnroEznwpJBMzEj+TREAkcseU0HlCCyw5wv02fEsxK4JpQnMre7oV0tArRm+Ys3QqQvXv9Y8EzlqvLx0zedh1FqqCaHesTkX9RIeLP5sA7FgClRFXnXHD2goeMMg5IylIsxSeU7kA5OIWkEQDe5uyJgWtGXec9PzEwx4QucyUmYYhTwKourMFmXtVmnsFmvslmzh5sdmdb8+n06rl4GnWn8x/n9/jntGsbbPY/qbn/VHQdnaQzE0l9vy6S9OeX8v3SP/vzMaIkAhuOKJ7ICJIyAovEhYITsKm8iVGDqkr7t6rr6jLYrkEGty4V9CfikBI5v7YN6zs9r8rVwGBZk2ProqqvGxZzmFDrVLmDs0PjytO4emSwluwytZ3O5R5LchaiPF4TWCZwAAwB239IYYiRycjJPJJb2t5qF+ms/suwkh6r58tDyV1fhk+ZCOL9Zbh9vmvcvmHfY/ebVMM/miQ3cdWocc+OJ8lb52qgJzk50CSfUJLdqkHUnvCmp2+zCX9+NAnvWS2buHxbcQwZ3z5ZemVzAaUoIN/wa4Fz3WAnl/wmjZpNfluvrJRIIywQoflBilS/MH7rwhxPsWUiq9knzRFVW+2TpZdbh2os3/TiqVGu9GLoYI3VPlmGcqgIApzDGmIxoMy6hgWl4Pggl5RG132TVg0vL3o9Jj9LACJiIgVLQDf5zQziyNOApbngRSBAD8e6SzUND+Gdax2fC0qlynLN9F3KpFxtL2FtvV57YiHrTdEbOllVDEV0s6qUA/pQpbP1zslrWxW9BD15VTyrdVX0mjPFohcsd6E9WX6OWcEDLBek05bKb10qvQqVUuXLTfCHVNs7uvqkgtPNz4eWbVs/wnKvfgM=7Vpdb5swFP01kbaHRgQCTR/bpB+T1q1bp/XZMi54NTYzZkn263cdTBJqt9uk8FG1Lwk+xhf7nHMxN2QUzLPVpUR5ei1iwka+F69GwWLk+5Mg9OBLI+sKmR3PKiCRNDYn7YBb+psY0IxLShqTonGiEoIpmjdBLDgnWDUwJKVYNk+7F6x51RwlxAJuMWI2ekdjlZpV1MvS+BWhSVpfeeKZngzVJxugSFEslntQcD4K5lIIVR1lqzlhmryal2rcxRO924lJwtW/DJh/vzpdnS3X8k7dfTr5nbKP11+OTJRfiJVmwWayal0zIEXJY6KDeKPgbJlSRW5zhHXvEjQHLFUZg9YEDu8FV0bECazwzJ5kfUUiFVntQWbSl0RkRMk1nGJ6p4a/dbO53KnhzwyW7imxPREZByTbyDuS4MDw9B+c+Q7OIqY0EbCSKNFHt0TCCmscLrPrqsG8Bi4oIxxlEOtUM78ZOf5RCL43PH9y9E0pc1GYwWeooHgbRPudgeV8712KeMwoT3aZQgUv3o/8uWaoVCnoQzHSqMF4vB8nQ5QrmCUH3SEc1Z8PFD9UIXOG1kQW1cgcsKNc6A7dJAq/dy/kkc/ADqpppkJJ8UDmggkJCBecaIdRxh5BBdhRTyRYhLvWNwHWXBz5T5lWwMI27ASLlMYx4YA1rd6Cl8Oml0OHlyOXl/22vBwcOP8Pn++Bb5N07CJpGrVF0tQi6XNOeGX9KsEOZmXEaAJOXDByryNo1iAt2amBM3CqvohThqZQh3drENhKTAKHEkFbQoSWEHNG9fr69mzkj8MmVzOHa12mbYuqyKKq3pJ6piqYHQ+Nq2OLq5vN9vWIqf10rp/lNGcxKtItgXUCY2AI2H4mhSFGriNnq0Q/Oo+rp1W/+tZhNT3eONKHmrupDs+FwunhMnxy8ti4U8fz1WTapRqzF5PkLq46Ne7Jy0ny3rmqC8r9LKcDzfJ7RvMrM4nWM951++024yd2FTrUlA+9vm1sV5+Dzfn+ybLLm1OodQH5Sn6WpLAN9uqy36VRx9lvl1dGpAVRiLJikCK1L0zUuzAvp9xykdXtneYF1Vv9k2UXXEM1VuT69alTruxyaLDG6p8sR0FUYkwK2EM8AZR5F7ChlJIMckvpdN93adXt9uLbBZl+EwKISqkWLAPd9Ns5iKObWPBCyRIr0MP3PnBLwyH86trGO4OaqLpec70BcynX2s+wvl2vEUYwkF3kKZFkzIkam/dY1c3qRgolsGCvVjNHjd2xZnbdaGuGN7vwODdqvYlX3QGnYd/i2XXsW8I9q1no9a6ZXdb+PeF04fvqpYt6l84ufN/S7VnNIr89zaC5+5PUpm/vr2bB+R8=5Vdbb5swGP01kdaHIsAhSR9zaZtO3U3Zmu3RBQe8GRsZsyT79fsMdgKB7iKRtdKesI/v55zP/higebq7lThL3oiIsIHvRrsBWgx830OBCx+N7CtkMp5UQCxpZDodgRX9QQxoxsUFjUje6KiEYIpmTTAUnJNQNTAspdg2u20Ea66a4Zi0gFWIWRtd00gl5hT2WBpfEhondmXPNS0ptp0NkCc4EtsahK4HaC6FUFUp3c0J0+RZXqpxN0+0HjYmCVd/MmCGxvLdF3TnB58/3F+t195aiEszy3fMCnNgs1m1twxIUfCI6EncAZptE6rIKsOhbt2C5oAlKmVQ86C4EVwZET044ay9SbsikYrsapDZ9C0RKVFyD11M69Dwt29Wt0c1/InBkpoSh47YOCA+zHwkCQqGp7/gzO/gbMSUJgJOMop16ZorqvYWh2WOTRbMLHBDGeE4hbmmMC0pRzpfc8Frw7MnR78vZCZyM3ha7p6YxV1JGFYgHbiR5DlYNYfiq1ASrCiPbU+qYaDCLbLINqiEpBUI0aek2J/CRIUX3fs7sQ+orJoe0fN9I3PBhASEC060cShjJ1AOLtPLokVwrH0U4LjFpf+UFwUYa8PKOEtoFBEOWNPBZ7Bo0LRo0GHRUZdF/R4s+vA6jaPHt7E3/+SjYDmfzpay97DuP4zRqE3SpCuOPXQuktpxPNeRQQCz4dublzGjMVhxwchGz6Bpo/DITA2cglX1Ip06NJXq365o3JbiwHpdirMpgdpKMKrP99ymHflO0Hx9/DZX4w6q+nh8OqkatqhaEQkHenaq0GT80riatLi6pzmYyhUbfVqG9/iR6XCXwEv5NOpX96JFZT3ebRKnSY1wnhwYthEeAoUgxy9iHObI9MzpLtY5s1OlqX711dNq/lxnpIua3GH5ajGaLc0muFBh0t914F25v9XNG/5L4a5awukcqcw/aH7MZvQ0eZX7wz1chFWqc8df5L19jrTDP7nIJx2JR5dwZ7vI7V9bTbkHLKkoTIb5WKhSGSyVFo5ynUky+G+TIs8SIokTQyLs5OWV5kA2CoQy5lRX3AKq1Sb+T3mHftCSd+j2Iy9Uj3+iZVvtfx5d/wQ=7Vtbd9o4EP41nNM+1MfYYMxjAk3DbrPNKT3lWWsLW41sObII0F+/GpABI2VDu/iSJXkI9ljW5ftGmgtDxx0lq08cZfEdCzHtOHa46rjjjuN03b4tP0Cy3kr8gb8VRJyEqtFeMCU/sRKq96IFCXFeaigYo4JkZWHA0hQHoiRDnLNludmc0fKoGYqwJpgGiOrSGQlFrFZRLAvkt5hEcTFy11ZPElQ0VoI8RiFbHojcjx13xBkT26tkNcIUwCtw2b5388zT3cQ4TsUpL8wmf1x99uzHv29+hN/Fw+RpxJ4+qF6eEF2oBavJinWBAGeLNMTQid1xr5cxEXiaoQCeLiXnUhaLhMq7rrycs1QoErtyhdf6JIsRMRd4dSBSk/6EWYIFX8sm6mlP4bcu3y73bDi+ksUHTOwaIqUB0a7nPUjyQuH0C5g5Bsw8KgAIuRIvgqsREjhim2X8hRJcNJDj7dsUwqwQ3BCKU2jtXoFGqy6sHzlLDzrInn3/fsEzlqvXpwyG3fUCLKjHjv2OwH+0EPF7c8dHKiCZEmWec8HZAx4xyriUpCzFQD6h9EiUS00haSQF/f3dNya1ZvzBeU6fmFSOOd3slZiEIU6lrKyFFahZv6xmfYOaeSY1c6pSM/fMW/P8W9Ez7MXBwABSz6sKpJ4G0lf8uMC5kMJvmHNE0jNqM6Ikkso4pngOPQBwRJqKKyVOpLLCIEYmylydX2E9g8Z2XQMZbmXnoq2RAScaWOyY5GAScZ6DuZX95FuTLYFfBEKi4tiTdhJVxUnjHDHnGc6aXq3M6W4AptKb4izPYsyxFUmrZAWUyEVbgewwt8YcLaHrO5RKSvmlcjewT3RHquNOd0feuDuNO6/f9L7TbXyZuxQLK8dcrtHKOBMsYNSabu7v1e2lkuc7jZOn+x5v5J1Int84ef038n6TvGGvcfI8jby91z+KF+lDB+bsoQQA2oS3q44zWu9E5ij3kLgiVwOYhiiPdwAXVAUSUGk6/4Us2UcGPSerCFJj1jYb5Ww/oVvA07Y8uASoe9B9ykQQny9G6PrHJs8QJJi4qy55MtC4G218E42OuoPb/stQGWPbypDyNaS2B1DjSLmDfsugGr4SpfKdhpEqzuT2K1XzUOlBcWFbPjMEKYupQFxopqYDXn2R68hfgc2ZU5LdqklUbH9MpNZrf1w9XL5HIoZ8FEkFkx8puHGOHQDXUiyHsQUnkQyUIZHt2Fr0bG9e4zsPpHjTfpdnOCBzYIquNwNA4xgSYIssRAJPUjkNEqp5ywXGLHyv6ch/9ytfVpxaMpbdYxNrSJ64taYsXT0Ab6fh8Ia6013vaahHu+1Eyvebtht6aNlWE9s4VHogd4cCDsfpdxDlF28vDQzVbC9fS7w2NH0ZWStSryZeax4qPV7bFtEkKLtj4cZlEoTp3whe2PY3EVXv9i86LqXaMozAz52TFNHO4de6cwbuMNp6swc85pb808j8//i1wzJtXbtr4K1fkWM7+XP8xR3PujN0f3vz9HP4+JR8OXtd1xlQOioecU1JI2OFjV0VSHooeI029VEzxikE+HdYIBmnoQpUt40lJO7AoLZVxWNGRlobjnmOVbahvRrTU0ao9HisLd6GP2gbVnpEpu9we/cVUgt9jnP7GcNT1LkyT8PIkR4Ktnbr1xg2G6HSQ7L2bv2msdKDsnK2Xj8ILjpff+TJmuir91jQI8W3mtITijRcA3GVFWmYIxA9ctQrbFRZ4q7CRpVsX3qJjcka18zeSxXBpvqoN/bUoVldgZS83f9ibPPs4Hd37sd/AA==7Vxbc6JIFP41Vu0+xOImmsdEJ5PU5jYxW8nuy1YvtEBsaAJt1Pz67cZGwT6ZyU7k4sQ8RDxAA993rn0aO+YwXHxNUOxfUReTjqG5i4456hiGbvY0/iEky5Vk0B+sBF4SuPKgjWAcvGIplOd5s8DFaelARilhQVwWOjSKsMNKMpQkdF4+bEJJ+aox8rAiGDuIqNKHwGW+fIr8sYT8HAeen19Z1+SeEOUHS0HqI5fOCyLzS8ccJpSy1Va4GGIiwMtxWZ139sbe9Y0lOGLvOeF5+nhFrizz4Q80DuNpfKedPh7JUV4QmckHljfLljkCCZ1FLhaDaB3zdO4HDI9j5Ii9c845l/ksJPybzjcnNGKSRJ0/4al6k/kVccLwoiCSN/0V0xCzZMkPkXtzqJflr/MNG2aOuV9gwrCkEEkN8NYjb0DiGxKn/4GZAWBmEyaA4E9ie2LrlqAlTvhRVyjiKhKKx5cH8WtujsuFcS44CwiOUMgHPhHAZcN0n1IaFU6P3zz7dpbENJUnn2SPwlA6TflngglinEZhP+uRxY7fPMxYEHnCiHyUIIdlN06ClN/yUEgTjNQDxJ40RvMI3MPdAIbPwcz5HX6WLb3j6sHKypWyhE7xkBKacElEIyw0LiBkS8RvyxHXNke9zbd7ylV1dGS8pcSUa+SEZAbqB66LIy4rq34Fum2XddtWddsYQLptVKXb5o79we7t3zRVkPo2AJJ+XJkD0BSUhNkK0/IDYVIhTlMRV/g46So2cdWdOSvzu4h2qOqIBB7X1BHBEzGCQDXgwetEikOuyeIiIE2Va3fP3KLOAvTbAqgzK2NOjXf31KXdJ/SCPi0r/V7TrKgR9dOzYtVpK0+pdv90dcHih9nx67l3823puTtPDStIBYF4WV0oAEFS4+UhErxDu3v9prXbOricbVJsrcZAAJLSO5CikGI3TYrd+jjQP248DgwOceBntHugNx0Hjg8uRyHFatrl5FpRYAUT7HCw09jHCe5GmHVTnPBn7MYJZdShpDvM55lupeSz8nesNW1UevtrB10z3hk01jNwu4fpUD38lILrWvPF8aF+UGnRjcYDx6GCAGgZ1EjLI+sfnV+Q8evlX8439BCf34VzoNgeFjpNl1mnaYucIit541QA5qLUX6OX8+BwtPhQ32GCjxGLkcOFJ/rU3VVr2Fh9imEFWFrXFpsCR0sMH1Hm+PJSOyBmXX2sJ15VL6ZDvOyikwnyolrLkARZq7LhYG3/GKl+nUCpNfA4yz4bB8rMbbstSPX3RKUsqI1YJ1DqjEFLVapxpNQSfRUxNDrh/4qRZIQZCkjaymAyIUF8Lm+i4sACMVZvYAEKeDXia3f4eYaB2N+GxKyCIAYULXqvzjRMN1RWxNoeUWB2DBuFAoZsYU6BLCmG1+i0wbAqNiYbmE2p2ZjUaYJ2xlQIqlojBVCVtzSoNg/VvuT+NtTkrxWpvUn+m4dKzf7HM8fBqZi9pCLyn/H8bJbgVgaSGjM0iKmag4paf6zTsVJOnWUIAW3nZPPu3Q20MkWHjKiyLA1YXToW66y/l6RpF6PPmKkZ3bIDHEApdq1mBSwwbUtYfQ9YtUYLQy1HWhNY+60DSy0Ccq9Q8gNR9nbHA02IW4HH/rFjqMdnK7oMFWn1em219Ojyv/ub0c2vywO3gS0ioLUHUFN9FzyMLfTPFUPe9Ob1yLKe/77XaG9vWhq6Dq0/qMijgEjtTVnTPFRAU6M46xxg1de2Id+qsaABOaos9QJJUguacmtAvI/7y3piJSLqGtQEqCokgoSofZu2uuJBw/4FaJe01hc3jpVaYf0ZEYpccbHsdBcxdbHPp/PHAE/1+mOg27R+K774orkbYDE9GIhSJpVar6VBOCNVzTvtwmdXsHBLV5Y5gu9+rD19LU4caD611TVpNU5/31jG9eIxGVwfvRiDf9PRWWxMgR+G2MfFhwoDAE/vX3xo1PkyAchKld3TD2G1nYYYNc4xgUhV2T39EFKKqTcOlTrNcI3nXHBJHThqtT//+BBDSv4BMbSr/IN/3fxsUrav8ONT5pf/AA==7VhdT9swFP01leCBKnGatjxCy5fEJLSiIR5N4iQGx84ch7b79bu3dZqGhMGkhoK2p9jH9rV9zrmJnZ43SRcXmmbJNxUy0SNOuOh50x4hruc78EBkuUbGo/EaiDUPbacKmPFfzIJ2XFzwkOW1jkYpYXhWBwMlJQtMDaNaq3m9W6REfdaMxqwBzAIqmugdD01id1FuC/FLxuOknNl1bEtKy84WyBMaqvkW5J31vIlWyqxL6WLCBJJX8rIed/5K62ZhmknzngHyKfvhBMHs6sI9ued3d6fFPT+yUZ6pKOyG7WLNsmRAq0KGDIM4Pe90nnDDZhkNsHUOmgOWmFRAzYVipKSxIrqww9PmIssZmTZssQXZRV8wlTKjl9DFtg4sf8t6dV6pQcYWS7aU2HSk1gHxJnJFEhQsT3/BGWnhbCgMEgE7GcZYulaalShMUjWUYFYC51wwSVOIdAJBBYzrP+ZKbg3OXh17U+hM5XbouUCzo8U1JommMo+Y5jK2YZGKB1UYbEywNlcakgEGaJVuwJxpkGaVZRssEBzlI85BJuiSPggENVgApptgkQkecyVtlZmg3z9s38ALb4EFTN1AudHqiU0UrBgQqSRDV3EhXkA5WBD35k39qnarwI7TI/KaURVsLRKrJEx4GDIJWN3eHfjXr/vXb/HvsM2/pCv/ejvO+d3nuDdskjRqJcnpiqRBg6Sbyvvf197fmZkp5A94cSpYhBGQNw6foBMLp+BVnKRViLpUu/erN2pq4XotWnhdSeE3pJjYF9KeXTskfb/+bSItvm2hqrNP07BB1cy+z/dMlTcefTauRm9lOD7Yz4LlTadt53h5rEMiQ5onG1bLrA6ANpDgD3kNMTKMnC5iPEX31wdXsn5iWOTM6Q+xiIQOMLxUJkh2l/bu8Xvc7A4+UqLx18n88Z7dfPyFMn/fXJV3yy2yrjlmuaMi3G7jkOsc4JH88FO+BiLBs0u7iJ2/Epw3hfvYFwJpKof3p9VNhaNQKctzvNFDHKwGcCkxuggMw4vOlWwo+BnObF3cOciLQ1yLcqRNuc4OcaT5p+FWhar/SJ/pv6pK2wf2g1Vp/sv4r8rY70wVqFY//FZtW79NvbPf
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java
index 1746ade6..1406ffd5 100644
--- a/src/main/java/electrosphere/engine/LoadingThread.java
+++ b/src/main/java/electrosphere/engine/LoadingThread.java
@@ -40,6 +40,7 @@ import electrosphere.game.simulation.MicroSimulation;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import electrosphere.menu.MenuGenerators;
+import electrosphere.menu.MenuGeneratorsMultiplayer;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.net.NetUtils;
@@ -64,6 +65,7 @@ import electrosphere.game.server.unit.UnitUtils;
import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.Window;
+import electrosphere.util.Utilities;
import java.util.LinkedList;
import java.util.List;
@@ -161,7 +163,7 @@ public class LoadingThread extends Thread {
//set simulations to ready if they exist
setSimulationsToReady();
//log
- LoggerInterface.loggerEngine.INFO("[Server]Finished loading");
+ LoggerInterface.loggerEngine.INFO("[Server]Finished loading main world");
break;
@@ -196,7 +198,7 @@ public class LoadingThread extends Thread {
creatingRandomEntities();
//set simulations to ready if they exist
setSimulationsToReady();
- LoggerInterface.loggerEngine.INFO("[Server]Finished loading");
+ LoggerInterface.loggerEngine.INFO("[Server]Finished loading arena world");
break;
case LOAD_CHARACTER_SERVER:
@@ -207,25 +209,26 @@ public class LoadingThread extends Thread {
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT);
//initialize the client thread (client)
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
- 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
loadingWindow.setVisible(false);
//make character creation window visible
WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true);
//recapture window
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
- LoggerInterface.loggerEngine.INFO("[Client]Finished loading");
- //set controls state
- Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME);
+ LoggerInterface.loggerEngine.INFO("[Client]Finished loading character creation menu");
+ //set menu controls again
+ Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.TITLE_MENU);
break;
case LOAD_CLIENT_WORLD:
@@ -265,7 +268,7 @@ public class LoadingThread extends Thread {
Globals.RENDER_FLAG_RENDER_UI = true;
Globals.RENDER_FLAG_RENDER_BLACK_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
Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME);
break;
diff --git a/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java b/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java
index 24e5cb3f..6a91255a 100644
--- a/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java
+++ b/src/main/java/electrosphere/game/data/creature/type/CreatureTypeLoader.java
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Semaphore;
public class CreatureTypeLoader {
@@ -11,6 +12,8 @@ public class CreatureTypeLoader {
List playableRaceNames = new LinkedList();
+ Semaphore playableRaceLock = new Semaphore(1);
+
public void putCreature(String name, CreatureType type){
creatureMap.put(name,type);
}
@@ -24,7 +27,21 @@ public class CreatureTypeLoader {
}
public List getPlayableRaces(){
- return playableRaceNames;
+ List races = null;
+ playableRaceLock.acquireUninterruptibly();
+ races = playableRaceNames;
+ playableRaceLock.release();
+ return races;
+ }
+
+ public void clearPlayableRaces(){
+ playableRaceNames.clear();
+ }
+
+ public void loadPlayableRaces(List races){
+ playableRaceLock.acquireUninterruptibly();
+ playableRaceNames = races;
+ playableRaceLock.release();
}
diff --git a/src/main/java/electrosphere/menu/MenuGenerators.java b/src/main/java/electrosphere/menu/MenuGenerators.java
index bf3286cd..b4828338 100644
--- a/src/main/java/electrosphere/menu/MenuGenerators.java
+++ b/src/main/java/electrosphere/menu/MenuGenerators.java
@@ -33,6 +33,7 @@ import electrosphere.renderer.ui.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.DraggableElement.DragEventCallback;
import electrosphere.renderer.ui.FocusableElement.FocusEventCallback;
import electrosphere.renderer.ui.NavigableElement.NavigationEventCallback;
+import electrosphere.renderer.ui.ValueElement.ValueChangeEventCallback;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
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.FocusEvent;
import electrosphere.renderer.ui.events.NavigationEvent;
+import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.form.FormElement;
import java.util.List;
@@ -100,7 +102,7 @@ public class MenuGenerators {
arenaButton.addChild(arenaLabel);
rVal.addChild(arenaButton);
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);
LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_ARENA);
Globals.loadingThreadsList.add(serverThread);
@@ -778,7 +780,8 @@ public class MenuGenerators {
int posY = offset * 350 + 100;
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));
- 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 range = attribute.getMaxValue() - minVal;
float actualValue = minVal + range * value;
diff --git a/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java b/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java
index 8bc3ed87..ef48ef33 100644
--- a/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java
+++ b/src/main/java/electrosphere/menu/MenuGeneratorsMultiplayer.java
@@ -1,51 +1,52 @@
package electrosphere.menu;
+import electrosphere.main.Globals;
+import electrosphere.renderer.ui.ClickableElement;
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;
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(){
FormElement rVal = new FormElement();
// int screenTop = Globals.WINDOW_HEIGHT - 150;
- List saveNames = SaveUtils.getSaves();
int verticalPosition = 125;
- for(String saveName : saveNames){
- if(!saveName.startsWith(".")){
- //button (select save)
- Button selectButton = new Button();
- Label selectLabel = new Label(100,125 + verticalPosition,1.0f);
- selectLabel.setText(saveName.toUpperCase());
- 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;
- }
+ //select race
+ StringCarousel raceCarousel = new StringCarousel(100, 125, 1.0f);
+ for(String raceName : Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces()){
+ raceCarousel.addOption(raceName);
}
+ rVal.addChild(raceCarousel);
//button (create)
Button createButton = new Button();
- Label createLabel = new Label(100,125 + verticalPosition + 200,1.0f);
- createLabel.setText("Create World");
+ Label createLabel = new Label(100,275,1.0f);
+ createLabel.setText("Create Character");
createButton.addChild(createLabel);
rVal.addChild(createButton);
createButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
diff --git a/src/main/java/electrosphere/net/client/ClientNetworking.java b/src/main/java/electrosphere/net/client/ClientNetworking.java
index 02901401..69aac6aa 100644
--- a/src/main/java/electrosphere/net/client/ClientNetworking.java
+++ b/src/main/java/electrosphere/net/client/ClientNetworking.java
@@ -22,6 +22,7 @@ import java.util.Properties;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec;
@@ -44,14 +45,30 @@ public class ClientNetworking implements Runnable{
NetworkParser parser;
ClientProtocol clientProtocol = new ClientProtocol();
+
+ static final int MAX_CONNECTION_ATTEMPTS = 10;
public ClientNetworking(String address, int port){
- try {
- this.socket = new Socket(address,port);
- } catch (IOException ex) {
- System.err.println("Failed to connect!");
- ex.printStackTrace();
- System.exit(1);
+ int connectionAttempts = 0;
+ boolean connected = false;
+ while(!connected){
+ try {
+ this.socket = new Socket(address,port);
+ } 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);
+ }
}
}
diff --git a/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java b/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java
index 4accaddb..38bff5e5 100644
--- a/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java
+++ b/src/main/java/electrosphere/net/client/protocol/AuthProtocol.java
@@ -4,6 +4,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage;
+import electrosphere.net.parser.net.message.LoreMessage;
import electrosphere.net.parser.net.message.TerrainMessage;
public class AuthProtocol {
@@ -17,8 +18,10 @@ public class AuthProtocol {
break;
case AUTHSUCCESS:
//trigger request to spawn character
- Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
- Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());
+ // Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
+ // Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());
+ //request playable races
+ Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage());
break;
case AUTHFAILURE:
//TODO: handle better
diff --git a/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java b/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java
index 283a1284..f8cb0524 100644
--- a/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java
+++ b/src/main/java/electrosphere/net/client/protocol/ClientProtocol.java
@@ -11,6 +11,7 @@ import electrosphere.main.Globals;
import electrosphere.main.Main;
import electrosphere.net.parser.net.message.AuthMessage;
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.PlayerMessage;
import electrosphere.net.parser.net.message.ServerMessage;
@@ -41,6 +42,9 @@ public class ClientProtocol {
case SERVER_MESSAGE:
ServerProtocol.handleServerMessage((ServerMessage)message);
break;
+ case LORE_MESSAGE:
+ LoreProtocol.handleLoreMessage((LoreMessage)message);
+ break;
}
}
diff --git a/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java b/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java
new file mode 100644
index 00000000..8f8f2ad0
--- /dev/null
+++ b/src/main/java/electrosphere/net/client/protocol/LoreProtocol.java
@@ -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 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;
+ }
+ }
+
+}
diff --git a/src/main/java/electrosphere/renderer/ui/ValueElement.java b/src/main/java/electrosphere/renderer/ui/ValueElement.java
new file mode 100644
index 00000000..91cb84a3
--- /dev/null
+++ b/src/main/java/electrosphere/renderer/ui/ValueElement.java
@@ -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);
+
+ }
+
+}
diff --git a/src/main/java/electrosphere/renderer/ui/elements/Slider.java b/src/main/java/electrosphere/renderer/ui/elements/Slider.java
index 5f740071..9badcfcd 100644
--- a/src/main/java/electrosphere/renderer/ui/elements/Slider.java
+++ b/src/main/java/electrosphere/renderer/ui/elements/Slider.java
@@ -11,6 +11,7 @@ import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.FocusableElement;
import electrosphere.renderer.ui.KeyEventElement;
import electrosphere.renderer.ui.MenuEventElement;
+import electrosphere.renderer.ui.ValueElement;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent;
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.MenuEvent;
import electrosphere.renderer.ui.events.MouseEvent;
+import electrosphere.renderer.ui.events.ValueChangeEvent;
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 height = 1;
@@ -41,7 +43,7 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
DragEventCallback onDragRelease;
ClickEventCallback onClick;
MenuEventCallback onMenuEvent;
- OnSliderChangeCallback onChangeValue;
+ ValueChangeEventCallback onValueChange;
float min = 0.0f;
float max = 1.0f;
@@ -238,8 +240,9 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
onMenuEvent = callback;
}
- public void setOnValueChange(OnSliderChangeCallback callback){
- this.onChangeValue = callback;
+ @Override
+ public void setOnValueChangeCallback(ValueChangeEventCallback callback) {
+ onValueChange = callback;
}
@@ -272,15 +275,15 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
switch(menuEvent.getType()){
case INCREMENT:
value = Math.min(value + ((max - min) * 0.01f),max);
- if(onChangeValue != null){
- onChangeValue.onChange(value);
+ if(onValueChange != null){
+ onValueChange.execute(new ValueChangeEvent(value));
}
propagate = false;
break;
case DECREMENT:
value = Math.max(value - ((max - min) * 0.01f),min);
- if(onChangeValue != null){
- onChangeValue.onChange(value);
+ if(onValueChange != null){
+ onValueChange.execute(new ValueChangeEvent(value));
}
propagate = false;
break;
@@ -320,8 +323,8 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
int percentage = clickEvent.getCurrentX() - positionX;
int max = width;
value = Math.max(Math.min((float)percentage/max,1.0f),0.0f);
- if(onChangeValue != null){
- onChangeValue.onChange(value);
+ if(onValueChange != null){
+ onValueChange.execute(new ValueChangeEvent(value));
}
propagate = false;
}
@@ -329,12 +332,5 @@ public class Slider implements ClickableElement, DraggableElement, FocusableElem
}
return propagate;
}
-
-
- public interface OnSliderChangeCallback {
-
- public void onChange(float value);
-
- }
}
diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java
new file mode 100644
index 00000000..1b837e0e
--- /dev/null
+++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java
@@ -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 options = new LinkedList();
+ int currentOption = -1;
+ String textCurrent = "";
+ int textPixelWidth = 0;
+
+ float fontSize = 1.0f;
+
+ List childrenElements = new LinkedList();
+
+ 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 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;
+ }
+
+}
diff --git a/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java
new file mode 100644
index 00000000..85580588
--- /dev/null
+++ b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java
@@ -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;
+ }
+
+}