From 6f7374c2fa58c8692b51018864b802e6b876d305 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sat, 23 Nov 2024 21:52:24 +0100 Subject: A new start --- .gitignore | 3 + LICENSE | 28 + Makefile | 19 + README.md | 3 + build.bat | 2 + data/fonts/Exo-Light.ttf | Bin 0 -> 88832 bytes data/fonts/Exo-LightItalic.ttf | Bin 0 -> 92716 bytes data/fonts/Exo-Medium.ttf | Bin 0 -> 88768 bytes data/fonts/Exo-MediumItalic.ttf | Bin 0 -> 92632 bytes data/fonts/Exo-Regular.ttf | Bin 0 -> 88752 bytes data/fonts/Exo-RegularItalic.ttf | Bin 0 -> 92528 bytes data/fonts/Exo-SemiBold.ttf | Bin 0 -> 88736 bytes data/fonts/Exo-SemiBoldItalic.ttf | Bin 0 -> 92612 bytes data/img/arrow-left-rounded.png | Bin 0 -> 4626 bytes data/img/arrow-left.png | Bin 0 -> 4012 bytes data/img/arrow-right.png | Bin 0 -> 3962 bytes data/img/back.png | Bin 0 -> 1057 bytes data/img/bank.png | Bin 0 -> 2531 bytes data/img/boat.png | Bin 0 -> 1280 bytes data/img/button_bottom.png | Bin 0 -> 156 bytes data/img/button_bottomleft.png | Bin 0 -> 321 bytes data/img/button_bottomright.png | Bin 0 -> 326 bytes data/img/button_left.png | Bin 0 -> 148 bytes data/img/button_right.png | Bin 0 -> 146 bytes data/img/button_top.png | Bin 0 -> 167 bytes data/img/button_topleft.png | Bin 0 -> 340 bytes data/img/button_topright.png | Bin 0 -> 341 bytes data/img/car-wheel.png | Bin 0 -> 29856 bytes data/img/checkmark.png | Bin 0 -> 6269 bytes data/img/city.png | Bin 0 -> 6824 bytes data/img/close.png | Bin 0 -> 1122 bytes data/img/coins.png | Bin 0 -> 13521 bytes data/img/denied.png | Bin 0 -> 56079 bytes data/img/dot.png | Bin 0 -> 326 bytes data/img/globe.png | Bin 0 -> 11344 bytes data/img/grid.png | Bin 0 -> 4612 bytes data/img/hired.png | Bin 0 -> 55479 bytes data/img/iveco-logo.png | Bin 0 -> 48172 bytes data/img/iveco-stralis-activespace.png | Bin 0 -> 397041 bytes data/img/iveco-stralis-hiway.png | Bin 0 -> 549910 bytes data/img/list.png | Bin 0 -> 1599 bytes data/img/location-pin.png | Bin 0 -> 7500 bytes data/img/location_dot.png | Bin 0 -> 381 bytes data/img/lock.png | Bin 0 -> 5082 bytes data/img/logo.png | Bin 0 -> 3537 bytes data/img/logo_fruitosis.png | Bin 0 -> 13879 bytes data/img/mercedes-logo.png | Bin 0 -> 170195 bytes data/img/panel_bottom.png | Bin 0 -> 134 bytes data/img/panel_bottomleft.png | Bin 0 -> 317 bytes data/img/panel_bottomright.png | Bin 0 -> 318 bytes data/img/panel_left.png | Bin 0 -> 129 bytes data/img/panel_right.png | Bin 0 -> 127 bytes data/img/panel_top.png | Bin 0 -> 135 bytes data/img/panel_topleft.png | Bin 0 -> 385 bytes data/img/panel_topright.png | Bin 0 -> 383 bytes data/img/pause.png | Bin 0 -> 6704 bytes data/img/portrait.png | Bin 0 -> 23822 bytes data/img/portrait/body.png | Bin 0 -> 3822 bytes data/img/portrait/face.png | Bin 0 -> 3790 bytes data/img/portrait/hair/hair1.png | Bin 0 -> 9891 bytes data/img/portrait/hair/hair2.png | Bin 0 -> 19798 bytes data/img/portrait/hair/hair3.png | Bin 0 -> 8301 bytes data/img/portrait/hair/hair4.png | Bin 0 -> 24610 bytes data/img/portrait/hair/hair5.png | Bin 0 -> 9567 bytes data/img/question-mark.png | Bin 0 -> 6033 bytes data/img/resume.png | Bin 0 -> 130052 bytes data/img/road.png | Bin 0 -> 7877 bytes data/img/signature.png | Bin 0 -> 47950 bytes data/img/star.png | Bin 0 -> 6314 bytes data/img/statistics.png | Bin 0 -> 2367 bytes data/img/tab-item.png | Bin 0 -> 674 bytes data/img/timer.png | Bin 0 -> 8881 bytes data/img/truck-unknown.png | Bin 0 -> 180435 bytes data/img/volvo-logo.png | Bin 0 -> 597610 bytes data/img/white.png | Bin 0 -> 117 bytes data/img/world-map.png | Bin 0 -> 1406536 bytes data/img/world_background.png | Bin 0 -> 5144901 bytes .../01 Meydan - Glimpse of Eternity.mp3 | Bin 0 -> 2866938 bytes .../Meydan - Havor/02 Meydan - Please wake up.mp3 | Bin 0 -> 3921619 bytes .../Meydan - Havor/03 Meydan - Tired of life.mp3 | Bin 0 -> 5589276 bytes .../Meydan - Havor/04 Meydan - Surreal Forest.mp3 | Bin 0 -> 4876655 bytes data/music/Meydan - Havor/05 Meydan - Rain.mp3 | Bin 0 -> 4286288 bytes .../06 Meydan - L'Etoile danse (Pt. 1).mp3 | Bin 0 -> 4715741 bytes .../07 Meydan - L'Etoile danse (Pt. 2).mp3 | Bin 0 -> 3499480 bytes data/music/Meydan - Havor/08 Meydan - Blind.mp3 | Bin 0 -> 4800378 bytes .../09 Meydan - Contemplate the stars.mp3 | Bin 0 -> 11681031 bytes data/music/Meydan - Havor/10 Meydan - Story.mp3 | Bin 0 -> 7920443 bytes data/music/Meydan - Havor/11 Meydan - Chalet.mp3 | Bin 0 -> 5589276 bytes data/music/Meydan - Havor/AUTHOR.txt | 1 + data/music/Meydan - Havor/LICENSE.txt | 7 + .../01 Nuno Adelaida - Amor e odio.mp3 | Bin 0 -> 5946382 bytes .../02 Nuno Adelaida - Children - Our Life.mp3 | Bin 0 -> 6292982 bytes .../03 Nuno Adelaida - F.Meridian.mp3 | Bin 0 -> 3281431 bytes .../04 Nuno Adelaida - I'm A Monster.mp3 | Bin 0 -> 6675218 bytes .../05 Nuno Adelaida - Mr.Nobody (B and W).mp3 | Bin 0 -> 6340632 bytes ...delaida - Never Too Late To Change Anything.mp3 | Bin 0 -> 5858580 bytes .../07 Nuno Adelaida - S.I.mp3 | Bin 0 -> 7165952 bytes .../08 Nuno Adelaida - Save Memories.mp3 | Bin 0 -> 4739598 bytes .../09 Nuno Adelaida - Sofia 30.mp3 | Bin 0 -> 4728740 bytes .../10 Nuno Adelaida - Zulmira.mp3 | Bin 0 -> 7231688 bytes data/music/Nuno Adelaida - Amentia/AUTHOR.txt | 1 + data/music/Nuno Adelaida - Amentia/LICENSE.txt | 4 + data/sounds/accelerate.wav | Bin 0 -> 132650 bytes data/sounds/click.wav | Bin 0 -> 11934 bytes data/sounds/click2.wav | Bin 0 -> 60390 bytes data/sounds/click3.wav | Bin 0 -> 24616 bytes data/sounds/event.wav | Bin 0 -> 31764 bytes data/sounds/return.wav | Bin 0 -> 60148 bytes data/world/boat-routes.json | 202 + data/world/companies.json | 52 + data/world/dealers.json | 79 + data/world/first-names.json | 4948 ++++++++++++++++++++ data/world/last-names.json | 4948 ++++++++++++++++++++ data/world/locations.json | 31 + docs/Screenshots/loading_screen.png | Bin 0 -> 74916 bytes docs/Screenshots/menu_screen.png | Bin 0 -> 4173100 bytes docs/Screenshots/menu_screen_loading.png | Bin 0 -> 3756755 bytes docs/Screenshots/save_state_select.png | Bin 0 -> 3772146 bytes docs/Screenshots/world_map.png | Bin 0 -> 4270114 bytes docs/Screenshots/world_map_purchase_location.png | Bin 0 -> 4065989 bytes docs/Specification.docx | Bin 0 -> 671623 bytes docs/Use Cases/use case diagram.png | Bin 0 -> 122259 bytes docs/Use Cases/use case diagram.uxf | 711 +++ docs/~$ecification.docx | Bin 0 -> 162 bytes libs/SDL2.lib | Bin 0 -> 165034 bytes libs/SDL2_mixer.lib | Bin 0 -> 17182 bytes libs/libFLAC-8.dll | Bin 0 -> 441344 bytes libs/libmodplug-1.dll | Bin 0 -> 252928 bytes libs/libmpg123-0.dll | Bin 0 -> 337408 bytes libs/libogg-0.dll | Bin 0 -> 52224 bytes libs/libopus-0.dll | Bin 0 -> 124928 bytes libs/libopusfile-0.dll | Bin 0 -> 46592 bytes libs/libvorbis-0.dll | Bin 0 -> 251904 bytes libs/libvorbisfile-3.dll | Bin 0 -> 69632 bytes src/data.c | 113 + src/game.c | 134 + src/include/data.h | 100 + src/include/game.h | 31 + src/include/scenery.h | 29 + src/include/scenes/error_scene.h | 15 + src/include/scenes/loading_scene.h | 15 + src/include/scenes/loading_world_scene.h | 16 + src/include/scenes/menu_scene.h | 15 + src/include/scenes/place_detail.h | 16 + src/include/scenes/save_state_select.h | 15 + src/include/scenes/settings_scene.h | 15 + src/include/scenes/world_map.h | 16 + src/include/settings.h | 28 + src/include/tooltip.h | 18 + src/include/ui/animation.h | 24 + src/include/ui/button.h | 20 + src/include/ui/colors.h | 62 + src/include/ui/panel.h | 15 + src/include/ui/portrait.h | 28 + src/include/ui/selectors.h | 13 + src/include/world.h | 383 ++ src/main.c | 182 + src/music.c | 22 + src/scenery.c | 53 + src/scenes/error_scene.c | 74 + src/scenes/loading_scene.c | 85 + src/scenes/loading_world_scene.c | 116 + src/scenes/menu_scene.c | 93 + src/scenes/place_detail.c | 1771 +++++++ src/scenes/save_state_select.c | 105 + src/scenes/settings_scene.c | 152 + src/scenes/world_map.c | 1094 +++++ src/tooltip.c | 22 + src/ui/animation.c | 18 + src/ui/button.c | 101 + src/ui/panel.c | 29 + src/ui/portrait.c | 21 + src/ui/selectors.c | 212 + src/world.c | 1321 ++++++ 174 files changed, 17631 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 build.bat create mode 100644 data/fonts/Exo-Light.ttf create mode 100644 data/fonts/Exo-LightItalic.ttf create mode 100644 data/fonts/Exo-Medium.ttf create mode 100644 data/fonts/Exo-MediumItalic.ttf create mode 100644 data/fonts/Exo-Regular.ttf create mode 100644 data/fonts/Exo-RegularItalic.ttf create mode 100644 data/fonts/Exo-SemiBold.ttf create mode 100644 data/fonts/Exo-SemiBoldItalic.ttf create mode 100644 data/img/arrow-left-rounded.png create mode 100644 data/img/arrow-left.png create mode 100644 data/img/arrow-right.png create mode 100644 data/img/back.png create mode 100644 data/img/bank.png create mode 100644 data/img/boat.png create mode 100644 data/img/button_bottom.png create mode 100644 data/img/button_bottomleft.png create mode 100644 data/img/button_bottomright.png create mode 100644 data/img/button_left.png create mode 100644 data/img/button_right.png create mode 100644 data/img/button_top.png create mode 100644 data/img/button_topleft.png create mode 100644 data/img/button_topright.png create mode 100644 data/img/car-wheel.png create mode 100644 data/img/checkmark.png create mode 100644 data/img/city.png create mode 100644 data/img/close.png create mode 100644 data/img/coins.png create mode 100644 data/img/denied.png create mode 100644 data/img/dot.png create mode 100644 data/img/globe.png create mode 100644 data/img/grid.png create mode 100644 data/img/hired.png create mode 100644 data/img/iveco-logo.png create mode 100644 data/img/iveco-stralis-activespace.png create mode 100644 data/img/iveco-stralis-hiway.png create mode 100644 data/img/list.png create mode 100644 data/img/location-pin.png create mode 100644 data/img/location_dot.png create mode 100644 data/img/lock.png create mode 100644 data/img/logo.png create mode 100644 data/img/logo_fruitosis.png create mode 100644 data/img/mercedes-logo.png create mode 100644 data/img/panel_bottom.png create mode 100644 data/img/panel_bottomleft.png create mode 100644 data/img/panel_bottomright.png create mode 100644 data/img/panel_left.png create mode 100644 data/img/panel_right.png create mode 100644 data/img/panel_top.png create mode 100644 data/img/panel_topleft.png create mode 100644 data/img/panel_topright.png create mode 100644 data/img/pause.png create mode 100644 data/img/portrait.png create mode 100644 data/img/portrait/body.png create mode 100644 data/img/portrait/face.png create mode 100644 data/img/portrait/hair/hair1.png create mode 100644 data/img/portrait/hair/hair2.png create mode 100644 data/img/portrait/hair/hair3.png create mode 100644 data/img/portrait/hair/hair4.png create mode 100644 data/img/portrait/hair/hair5.png create mode 100644 data/img/question-mark.png create mode 100644 data/img/resume.png create mode 100644 data/img/road.png create mode 100644 data/img/signature.png create mode 100644 data/img/star.png create mode 100644 data/img/statistics.png create mode 100644 data/img/tab-item.png create mode 100644 data/img/timer.png create mode 100644 data/img/truck-unknown.png create mode 100644 data/img/volvo-logo.png create mode 100644 data/img/white.png create mode 100644 data/img/world-map.png create mode 100644 data/img/world_background.png create mode 100644 data/music/Meydan - Havor/01 Meydan - Glimpse of Eternity.mp3 create mode 100644 data/music/Meydan - Havor/02 Meydan - Please wake up.mp3 create mode 100644 data/music/Meydan - Havor/03 Meydan - Tired of life.mp3 create mode 100644 data/music/Meydan - Havor/04 Meydan - Surreal Forest.mp3 create mode 100644 data/music/Meydan - Havor/05 Meydan - Rain.mp3 create mode 100644 data/music/Meydan - Havor/06 Meydan - L'Etoile danse (Pt. 1).mp3 create mode 100644 data/music/Meydan - Havor/07 Meydan - L'Etoile danse (Pt. 2).mp3 create mode 100644 data/music/Meydan - Havor/08 Meydan - Blind.mp3 create mode 100644 data/music/Meydan - Havor/09 Meydan - Contemplate the stars.mp3 create mode 100644 data/music/Meydan - Havor/10 Meydan - Story.mp3 create mode 100644 data/music/Meydan - Havor/11 Meydan - Chalet.mp3 create mode 100644 data/music/Meydan - Havor/AUTHOR.txt create mode 100644 data/music/Meydan - Havor/LICENSE.txt create mode 100644 data/music/Nuno Adelaida - Amentia/01 Nuno Adelaida - Amor e odio.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/02 Nuno Adelaida - Children - Our Life.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/03 Nuno Adelaida - F.Meridian.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/04 Nuno Adelaida - I'm A Monster.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/05 Nuno Adelaida - Mr.Nobody (B and W).mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/06 Nuno Adelaida - Never Too Late To Change Anything.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/07 Nuno Adelaida - S.I.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/08 Nuno Adelaida - Save Memories.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/09 Nuno Adelaida - Sofia 30.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/10 Nuno Adelaida - Zulmira.mp3 create mode 100644 data/music/Nuno Adelaida - Amentia/AUTHOR.txt create mode 100644 data/music/Nuno Adelaida - Amentia/LICENSE.txt create mode 100644 data/sounds/accelerate.wav create mode 100644 data/sounds/click.wav create mode 100644 data/sounds/click2.wav create mode 100644 data/sounds/click3.wav create mode 100644 data/sounds/event.wav create mode 100644 data/sounds/return.wav create mode 100644 data/world/boat-routes.json create mode 100644 data/world/companies.json create mode 100644 data/world/dealers.json create mode 100644 data/world/first-names.json create mode 100644 data/world/last-names.json create mode 100644 data/world/locations.json create mode 100644 docs/Screenshots/loading_screen.png create mode 100644 docs/Screenshots/menu_screen.png create mode 100644 docs/Screenshots/menu_screen_loading.png create mode 100644 docs/Screenshots/save_state_select.png create mode 100644 docs/Screenshots/world_map.png create mode 100644 docs/Screenshots/world_map_purchase_location.png create mode 100644 docs/Specification.docx create mode 100644 docs/Use Cases/use case diagram.png create mode 100644 docs/Use Cases/use case diagram.uxf create mode 100644 docs/~$ecification.docx create mode 100644 libs/SDL2.lib create mode 100644 libs/SDL2_mixer.lib create mode 100644 libs/libFLAC-8.dll create mode 100644 libs/libmodplug-1.dll create mode 100644 libs/libmpg123-0.dll create mode 100644 libs/libogg-0.dll create mode 100644 libs/libopus-0.dll create mode 100644 libs/libopusfile-0.dll create mode 100644 libs/libvorbis-0.dll create mode 100644 libs/libvorbisfile-3.dll create mode 100644 src/data.c create mode 100644 src/game.c create mode 100644 src/include/data.h create mode 100644 src/include/game.h create mode 100644 src/include/scenery.h create mode 100644 src/include/scenes/error_scene.h create mode 100644 src/include/scenes/loading_scene.h create mode 100644 src/include/scenes/loading_world_scene.h create mode 100644 src/include/scenes/menu_scene.h create mode 100644 src/include/scenes/place_detail.h create mode 100644 src/include/scenes/save_state_select.h create mode 100644 src/include/scenes/settings_scene.h create mode 100644 src/include/scenes/world_map.h create mode 100644 src/include/settings.h create mode 100644 src/include/tooltip.h create mode 100644 src/include/ui/animation.h create mode 100644 src/include/ui/button.h create mode 100644 src/include/ui/colors.h create mode 100644 src/include/ui/panel.h create mode 100644 src/include/ui/portrait.h create mode 100644 src/include/ui/selectors.h create mode 100644 src/include/world.h create mode 100644 src/main.c create mode 100644 src/music.c create mode 100644 src/scenery.c create mode 100644 src/scenes/error_scene.c create mode 100644 src/scenes/loading_scene.c create mode 100644 src/scenes/loading_world_scene.c create mode 100644 src/scenes/menu_scene.c create mode 100644 src/scenes/place_detail.c create mode 100644 src/scenes/save_state_select.c create mode 100644 src/scenes/settings_scene.c create mode 100644 src/scenes/world_map.c create mode 100644 src/tooltip.c create mode 100644 src/ui/animation.c create mode 100644 src/ui/button.c create mode 100644 src/ui/panel.c create mode 100644 src/ui/portrait.c create mode 100644 src/ui/selectors.c create mode 100644 src/world.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a661307 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/ +build/ +art/*.psd \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b99ec5e --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, Aldrik Ramaekers + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e82448 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ + +main: + mkdir -p "build/" + cp -a "data/." "build/data" + gcc -m64 -Wall -g -DMODE_DEBUG src/main.c -o build/truckerx.exe -Llibs/ -lprojectbase-debug -lSDL2 -lSDL2_mixer + ./build/truckerx.exe + +debug: + mkdir -p "build/" + cp -a "data/." "build/data" + gcc -m64 -Wall -g -DMODE_DEBUG src/main.c -o build/truckerx.exe -Llibs/ -lprojectbase-debug -lSDL2 -lSDL2_mixer + gdb -q -ex='set confirm on' -ex=run -ex=quit --args ./build/truckerx.exe + +linux: + mkdir -p "build/" + cp -a "data/." "build/data" + gcc -m64 -Wall -g -DMODE_DEBUG src/main.c -o build/truckerx -lprojectbase-debug -lX11 -lm -ldl -lSDL2 -lSDL2_mixer + sudo chmod +x ./build/truckerx + ./build/truckerx \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b3d1d4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Truck logistics management game written using C + OpenGL. Uses SDL2 SDL2_Mixer for audio. + +Video: https://www.youtube.com/watch?v=vd9a-LMGzrs&ab_channel=Aldrik diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..5a407c9 --- /dev/null +++ b/build.bat @@ -0,0 +1,2 @@ +gcc.exe -m64 -w -g -DMODE_DEBUG src/main.c -o build/truckerx2.exe -Llibs/ -lSDL2 -lSDL2_mixer +call build\truckerx2.exe \ No newline at end of file diff --git a/data/fonts/Exo-Light.ttf b/data/fonts/Exo-Light.ttf new file mode 100644 index 0000000..b539e75 Binary files /dev/null and b/data/fonts/Exo-Light.ttf differ diff --git a/data/fonts/Exo-LightItalic.ttf b/data/fonts/Exo-LightItalic.ttf new file mode 100644 index 0000000..494f1dd Binary files /dev/null and b/data/fonts/Exo-LightItalic.ttf differ diff --git a/data/fonts/Exo-Medium.ttf b/data/fonts/Exo-Medium.ttf new file mode 100644 index 0000000..dccd527 Binary files /dev/null and b/data/fonts/Exo-Medium.ttf differ diff --git a/data/fonts/Exo-MediumItalic.ttf b/data/fonts/Exo-MediumItalic.ttf new file mode 100644 index 0000000..573fbe7 Binary files /dev/null and b/data/fonts/Exo-MediumItalic.ttf differ diff --git a/data/fonts/Exo-Regular.ttf b/data/fonts/Exo-Regular.ttf new file mode 100644 index 0000000..e370360 Binary files /dev/null and b/data/fonts/Exo-Regular.ttf differ diff --git a/data/fonts/Exo-RegularItalic.ttf b/data/fonts/Exo-RegularItalic.ttf new file mode 100644 index 0000000..b0e0121 Binary files /dev/null and b/data/fonts/Exo-RegularItalic.ttf differ diff --git a/data/fonts/Exo-SemiBold.ttf b/data/fonts/Exo-SemiBold.ttf new file mode 100644 index 0000000..f61ce97 Binary files /dev/null and b/data/fonts/Exo-SemiBold.ttf differ diff --git a/data/fonts/Exo-SemiBoldItalic.ttf b/data/fonts/Exo-SemiBoldItalic.ttf new file mode 100644 index 0000000..13b4907 Binary files /dev/null and b/data/fonts/Exo-SemiBoldItalic.ttf differ diff --git a/data/img/arrow-left-rounded.png b/data/img/arrow-left-rounded.png new file mode 100644 index 0000000..1036fb5 Binary files /dev/null and b/data/img/arrow-left-rounded.png differ diff --git a/data/img/arrow-left.png b/data/img/arrow-left.png new file mode 100644 index 0000000..1c9048b Binary files /dev/null and b/data/img/arrow-left.png differ diff --git a/data/img/arrow-right.png b/data/img/arrow-right.png new file mode 100644 index 0000000..98eee02 Binary files /dev/null and b/data/img/arrow-right.png differ diff --git a/data/img/back.png b/data/img/back.png new file mode 100644 index 0000000..a3b04da Binary files /dev/null and b/data/img/back.png differ diff --git a/data/img/bank.png b/data/img/bank.png new file mode 100644 index 0000000..08f8548 Binary files /dev/null and b/data/img/bank.png differ diff --git a/data/img/boat.png b/data/img/boat.png new file mode 100644 index 0000000..a8b71f5 Binary files /dev/null and b/data/img/boat.png differ diff --git a/data/img/button_bottom.png b/data/img/button_bottom.png new file mode 100644 index 0000000..4965d4e Binary files /dev/null and b/data/img/button_bottom.png differ diff --git a/data/img/button_bottomleft.png b/data/img/button_bottomleft.png new file mode 100644 index 0000000..354dafd Binary files /dev/null and b/data/img/button_bottomleft.png differ diff --git a/data/img/button_bottomright.png b/data/img/button_bottomright.png new file mode 100644 index 0000000..e3de11f Binary files /dev/null and b/data/img/button_bottomright.png differ diff --git a/data/img/button_left.png b/data/img/button_left.png new file mode 100644 index 0000000..194d8ae Binary files /dev/null and b/data/img/button_left.png differ diff --git a/data/img/button_right.png b/data/img/button_right.png new file mode 100644 index 0000000..163f85d Binary files /dev/null and b/data/img/button_right.png differ diff --git a/data/img/button_top.png b/data/img/button_top.png new file mode 100644 index 0000000..398aca3 Binary files /dev/null and b/data/img/button_top.png differ diff --git a/data/img/button_topleft.png b/data/img/button_topleft.png new file mode 100644 index 0000000..fdb8bc6 Binary files /dev/null and b/data/img/button_topleft.png differ diff --git a/data/img/button_topright.png b/data/img/button_topright.png new file mode 100644 index 0000000..d465193 Binary files /dev/null and b/data/img/button_topright.png differ diff --git a/data/img/car-wheel.png b/data/img/car-wheel.png new file mode 100644 index 0000000..f8d98aa Binary files /dev/null and b/data/img/car-wheel.png differ diff --git a/data/img/checkmark.png b/data/img/checkmark.png new file mode 100644 index 0000000..6f94bfe Binary files /dev/null and b/data/img/checkmark.png differ diff --git a/data/img/city.png b/data/img/city.png new file mode 100644 index 0000000..5f71ee3 Binary files /dev/null and b/data/img/city.png differ diff --git a/data/img/close.png b/data/img/close.png new file mode 100644 index 0000000..ec2af21 Binary files /dev/null and b/data/img/close.png differ diff --git a/data/img/coins.png b/data/img/coins.png new file mode 100644 index 0000000..12b55a1 Binary files /dev/null and b/data/img/coins.png differ diff --git a/data/img/denied.png b/data/img/denied.png new file mode 100644 index 0000000..f4162c0 Binary files /dev/null and b/data/img/denied.png differ diff --git a/data/img/dot.png b/data/img/dot.png new file mode 100644 index 0000000..120abfd Binary files /dev/null and b/data/img/dot.png differ diff --git a/data/img/globe.png b/data/img/globe.png new file mode 100644 index 0000000..28ae02c Binary files /dev/null and b/data/img/globe.png differ diff --git a/data/img/grid.png b/data/img/grid.png new file mode 100644 index 0000000..2fc522c Binary files /dev/null and b/data/img/grid.png differ diff --git a/data/img/hired.png b/data/img/hired.png new file mode 100644 index 0000000..b015934 Binary files /dev/null and b/data/img/hired.png differ diff --git a/data/img/iveco-logo.png b/data/img/iveco-logo.png new file mode 100644 index 0000000..cf022d6 Binary files /dev/null and b/data/img/iveco-logo.png differ diff --git a/data/img/iveco-stralis-activespace.png b/data/img/iveco-stralis-activespace.png new file mode 100644 index 0000000..d72abbd Binary files /dev/null and b/data/img/iveco-stralis-activespace.png differ diff --git a/data/img/iveco-stralis-hiway.png b/data/img/iveco-stralis-hiway.png new file mode 100644 index 0000000..3f01f83 Binary files /dev/null and b/data/img/iveco-stralis-hiway.png differ diff --git a/data/img/list.png b/data/img/list.png new file mode 100644 index 0000000..30db793 Binary files /dev/null and b/data/img/list.png differ diff --git a/data/img/location-pin.png b/data/img/location-pin.png new file mode 100644 index 0000000..ec06a40 Binary files /dev/null and b/data/img/location-pin.png differ diff --git a/data/img/location_dot.png b/data/img/location_dot.png new file mode 100644 index 0000000..a3b59fb Binary files /dev/null and b/data/img/location_dot.png differ diff --git a/data/img/lock.png b/data/img/lock.png new file mode 100644 index 0000000..4f5db35 Binary files /dev/null and b/data/img/lock.png differ diff --git a/data/img/logo.png b/data/img/logo.png new file mode 100644 index 0000000..97dd758 Binary files /dev/null and b/data/img/logo.png differ diff --git a/data/img/logo_fruitosis.png b/data/img/logo_fruitosis.png new file mode 100644 index 0000000..118fcb1 Binary files /dev/null and b/data/img/logo_fruitosis.png differ diff --git a/data/img/mercedes-logo.png b/data/img/mercedes-logo.png new file mode 100644 index 0000000..a1c21bf Binary files /dev/null and b/data/img/mercedes-logo.png differ diff --git a/data/img/panel_bottom.png b/data/img/panel_bottom.png new file mode 100644 index 0000000..effcad7 Binary files /dev/null and b/data/img/panel_bottom.png differ diff --git a/data/img/panel_bottomleft.png b/data/img/panel_bottomleft.png new file mode 100644 index 0000000..2a224c4 Binary files /dev/null and b/data/img/panel_bottomleft.png differ diff --git a/data/img/panel_bottomright.png b/data/img/panel_bottomright.png new file mode 100644 index 0000000..3da5051 Binary files /dev/null and b/data/img/panel_bottomright.png differ diff --git a/data/img/panel_left.png b/data/img/panel_left.png new file mode 100644 index 0000000..57207f8 Binary files /dev/null and b/data/img/panel_left.png differ diff --git a/data/img/panel_right.png b/data/img/panel_right.png new file mode 100644 index 0000000..7688684 Binary files /dev/null and b/data/img/panel_right.png differ diff --git a/data/img/panel_top.png b/data/img/panel_top.png new file mode 100644 index 0000000..a664976 Binary files /dev/null and b/data/img/panel_top.png differ diff --git a/data/img/panel_topleft.png b/data/img/panel_topleft.png new file mode 100644 index 0000000..18d3315 Binary files /dev/null and b/data/img/panel_topleft.png differ diff --git a/data/img/panel_topright.png b/data/img/panel_topright.png new file mode 100644 index 0000000..7f92e90 Binary files /dev/null and b/data/img/panel_topright.png differ diff --git a/data/img/pause.png b/data/img/pause.png new file mode 100644 index 0000000..b2753f1 Binary files /dev/null and b/data/img/pause.png differ diff --git a/data/img/portrait.png b/data/img/portrait.png new file mode 100644 index 0000000..840b054 Binary files /dev/null and b/data/img/portrait.png differ diff --git a/data/img/portrait/body.png b/data/img/portrait/body.png new file mode 100644 index 0000000..34369f8 Binary files /dev/null and b/data/img/portrait/body.png differ diff --git a/data/img/portrait/face.png b/data/img/portrait/face.png new file mode 100644 index 0000000..e76c5b0 Binary files /dev/null and b/data/img/portrait/face.png differ diff --git a/data/img/portrait/hair/hair1.png b/data/img/portrait/hair/hair1.png new file mode 100644 index 0000000..8fab725 Binary files /dev/null and b/data/img/portrait/hair/hair1.png differ diff --git a/data/img/portrait/hair/hair2.png b/data/img/portrait/hair/hair2.png new file mode 100644 index 0000000..3ea9865 Binary files /dev/null and b/data/img/portrait/hair/hair2.png differ diff --git a/data/img/portrait/hair/hair3.png b/data/img/portrait/hair/hair3.png new file mode 100644 index 0000000..c327380 Binary files /dev/null and b/data/img/portrait/hair/hair3.png differ diff --git a/data/img/portrait/hair/hair4.png b/data/img/portrait/hair/hair4.png new file mode 100644 index 0000000..57f3284 Binary files /dev/null and b/data/img/portrait/hair/hair4.png differ diff --git a/data/img/portrait/hair/hair5.png b/data/img/portrait/hair/hair5.png new file mode 100644 index 0000000..24dec4f Binary files /dev/null and b/data/img/portrait/hair/hair5.png differ diff --git a/data/img/question-mark.png b/data/img/question-mark.png new file mode 100644 index 0000000..f30122f Binary files /dev/null and b/data/img/question-mark.png differ diff --git a/data/img/resume.png b/data/img/resume.png new file mode 100644 index 0000000..4686fa6 Binary files /dev/null and b/data/img/resume.png differ diff --git a/data/img/road.png b/data/img/road.png new file mode 100644 index 0000000..4274ee8 Binary files /dev/null and b/data/img/road.png differ diff --git a/data/img/signature.png b/data/img/signature.png new file mode 100644 index 0000000..7a1729f Binary files /dev/null and b/data/img/signature.png differ diff --git a/data/img/star.png b/data/img/star.png new file mode 100644 index 0000000..ac6b582 Binary files /dev/null and b/data/img/star.png differ diff --git a/data/img/statistics.png b/data/img/statistics.png new file mode 100644 index 0000000..375d5d5 Binary files /dev/null and b/data/img/statistics.png differ diff --git a/data/img/tab-item.png b/data/img/tab-item.png new file mode 100644 index 0000000..7d2a601 Binary files /dev/null and b/data/img/tab-item.png differ diff --git a/data/img/timer.png b/data/img/timer.png new file mode 100644 index 0000000..73bbf03 Binary files /dev/null and b/data/img/timer.png differ diff --git a/data/img/truck-unknown.png b/data/img/truck-unknown.png new file mode 100644 index 0000000..7ddc679 Binary files /dev/null and b/data/img/truck-unknown.png differ diff --git a/data/img/volvo-logo.png b/data/img/volvo-logo.png new file mode 100644 index 0000000..897c93f Binary files /dev/null and b/data/img/volvo-logo.png differ diff --git a/data/img/white.png b/data/img/white.png new file mode 100644 index 0000000..fc25c77 Binary files /dev/null and b/data/img/white.png differ diff --git a/data/img/world-map.png b/data/img/world-map.png new file mode 100644 index 0000000..ca1c263 Binary files /dev/null and b/data/img/world-map.png differ diff --git a/data/img/world_background.png b/data/img/world_background.png new file mode 100644 index 0000000..cbfa5de Binary files /dev/null and b/data/img/world_background.png differ diff --git a/data/music/Meydan - Havor/01 Meydan - Glimpse of Eternity.mp3 b/data/music/Meydan - Havor/01 Meydan - Glimpse of Eternity.mp3 new file mode 100644 index 0000000..0615464 Binary files /dev/null and b/data/music/Meydan - Havor/01 Meydan - Glimpse of Eternity.mp3 differ diff --git a/data/music/Meydan - Havor/02 Meydan - Please wake up.mp3 b/data/music/Meydan - Havor/02 Meydan - Please wake up.mp3 new file mode 100644 index 0000000..6756684 Binary files /dev/null and b/data/music/Meydan - Havor/02 Meydan - Please wake up.mp3 differ diff --git a/data/music/Meydan - Havor/03 Meydan - Tired of life.mp3 b/data/music/Meydan - Havor/03 Meydan - Tired of life.mp3 new file mode 100644 index 0000000..f59b1e5 Binary files /dev/null and b/data/music/Meydan - Havor/03 Meydan - Tired of life.mp3 differ diff --git a/data/music/Meydan - Havor/04 Meydan - Surreal Forest.mp3 b/data/music/Meydan - Havor/04 Meydan - Surreal Forest.mp3 new file mode 100644 index 0000000..f286010 Binary files /dev/null and b/data/music/Meydan - Havor/04 Meydan - Surreal Forest.mp3 differ diff --git a/data/music/Meydan - Havor/05 Meydan - Rain.mp3 b/data/music/Meydan - Havor/05 Meydan - Rain.mp3 new file mode 100644 index 0000000..47fa9d4 Binary files /dev/null and b/data/music/Meydan - Havor/05 Meydan - Rain.mp3 differ diff --git a/data/music/Meydan - Havor/06 Meydan - L'Etoile danse (Pt. 1).mp3 b/data/music/Meydan - Havor/06 Meydan - L'Etoile danse (Pt. 1).mp3 new file mode 100644 index 0000000..5fc611a Binary files /dev/null and b/data/music/Meydan - Havor/06 Meydan - L'Etoile danse (Pt. 1).mp3 differ diff --git a/data/music/Meydan - Havor/07 Meydan - L'Etoile danse (Pt. 2).mp3 b/data/music/Meydan - Havor/07 Meydan - L'Etoile danse (Pt. 2).mp3 new file mode 100644 index 0000000..b4b495a Binary files /dev/null and b/data/music/Meydan - Havor/07 Meydan - L'Etoile danse (Pt. 2).mp3 differ diff --git a/data/music/Meydan - Havor/08 Meydan - Blind.mp3 b/data/music/Meydan - Havor/08 Meydan - Blind.mp3 new file mode 100644 index 0000000..619b955 Binary files /dev/null and b/data/music/Meydan - Havor/08 Meydan - Blind.mp3 differ diff --git a/data/music/Meydan - Havor/09 Meydan - Contemplate the stars.mp3 b/data/music/Meydan - Havor/09 Meydan - Contemplate the stars.mp3 new file mode 100644 index 0000000..f9d4f71 Binary files /dev/null and b/data/music/Meydan - Havor/09 Meydan - Contemplate the stars.mp3 differ diff --git a/data/music/Meydan - Havor/10 Meydan - Story.mp3 b/data/music/Meydan - Havor/10 Meydan - Story.mp3 new file mode 100644 index 0000000..5f7809b Binary files /dev/null and b/data/music/Meydan - Havor/10 Meydan - Story.mp3 differ diff --git a/data/music/Meydan - Havor/11 Meydan - Chalet.mp3 b/data/music/Meydan - Havor/11 Meydan - Chalet.mp3 new file mode 100644 index 0000000..9a167e9 Binary files /dev/null and b/data/music/Meydan - Havor/11 Meydan - Chalet.mp3 differ diff --git a/data/music/Meydan - Havor/AUTHOR.txt b/data/music/Meydan - Havor/AUTHOR.txt new file mode 100644 index 0000000..3be1aee --- /dev/null +++ b/data/music/Meydan - Havor/AUTHOR.txt @@ -0,0 +1 @@ +Meydän \ No newline at end of file diff --git a/data/music/Meydan - Havor/LICENSE.txt b/data/music/Meydan - Havor/LICENSE.txt new file mode 100644 index 0000000..939bca6 --- /dev/null +++ b/data/music/Meydan - Havor/LICENSE.txt @@ -0,0 +1,7 @@ +The songs in this album are licensed under Attribution 4.0 International (CC BY 4.0) +Artist: https://freemusicarchive.org/music/Meydan +Album: https://freemusicarchive.org/music/Meydan/Havor +License: https://creativecommons.org/licenses/by/4.0/ + +Modifications: +"01 Meydan - Glimpse of Eternity" has been trimmed to remove periods of no sound. \ No newline at end of file diff --git a/data/music/Nuno Adelaida - Amentia/01 Nuno Adelaida - Amor e odio.mp3 b/data/music/Nuno Adelaida - Amentia/01 Nuno Adelaida - Amor e odio.mp3 new file mode 100644 index 0000000..64105b7 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/01 Nuno Adelaida - Amor e odio.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/02 Nuno Adelaida - Children - Our Life.mp3 b/data/music/Nuno Adelaida - Amentia/02 Nuno Adelaida - Children - Our Life.mp3 new file mode 100644 index 0000000..1bfc14a Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/02 Nuno Adelaida - Children - Our Life.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/03 Nuno Adelaida - F.Meridian.mp3 b/data/music/Nuno Adelaida - Amentia/03 Nuno Adelaida - F.Meridian.mp3 new file mode 100644 index 0000000..5fe8f97 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/03 Nuno Adelaida - F.Meridian.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/04 Nuno Adelaida - I'm A Monster.mp3 b/data/music/Nuno Adelaida - Amentia/04 Nuno Adelaida - I'm A Monster.mp3 new file mode 100644 index 0000000..f942ea2 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/04 Nuno Adelaida - I'm A Monster.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/05 Nuno Adelaida - Mr.Nobody (B and W).mp3 b/data/music/Nuno Adelaida - Amentia/05 Nuno Adelaida - Mr.Nobody (B and W).mp3 new file mode 100644 index 0000000..3f32974 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/05 Nuno Adelaida - Mr.Nobody (B and W).mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/06 Nuno Adelaida - Never Too Late To Change Anything.mp3 b/data/music/Nuno Adelaida - Amentia/06 Nuno Adelaida - Never Too Late To Change Anything.mp3 new file mode 100644 index 0000000..849b5e3 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/06 Nuno Adelaida - Never Too Late To Change Anything.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/07 Nuno Adelaida - S.I.mp3 b/data/music/Nuno Adelaida - Amentia/07 Nuno Adelaida - S.I.mp3 new file mode 100644 index 0000000..9baec6e Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/07 Nuno Adelaida - S.I.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/08 Nuno Adelaida - Save Memories.mp3 b/data/music/Nuno Adelaida - Amentia/08 Nuno Adelaida - Save Memories.mp3 new file mode 100644 index 0000000..53ba951 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/08 Nuno Adelaida - Save Memories.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/09 Nuno Adelaida - Sofia 30.mp3 b/data/music/Nuno Adelaida - Amentia/09 Nuno Adelaida - Sofia 30.mp3 new file mode 100644 index 0000000..6de9e71 Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/09 Nuno Adelaida - Sofia 30.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/10 Nuno Adelaida - Zulmira.mp3 b/data/music/Nuno Adelaida - Amentia/10 Nuno Adelaida - Zulmira.mp3 new file mode 100644 index 0000000..60315aa Binary files /dev/null and b/data/music/Nuno Adelaida - Amentia/10 Nuno Adelaida - Zulmira.mp3 differ diff --git a/data/music/Nuno Adelaida - Amentia/AUTHOR.txt b/data/music/Nuno Adelaida - Amentia/AUTHOR.txt new file mode 100644 index 0000000..49bd42f --- /dev/null +++ b/data/music/Nuno Adelaida - Amentia/AUTHOR.txt @@ -0,0 +1 @@ +Nuno Adelaida \ No newline at end of file diff --git a/data/music/Nuno Adelaida - Amentia/LICENSE.txt b/data/music/Nuno Adelaida - Amentia/LICENSE.txt new file mode 100644 index 0000000..bcd3702 --- /dev/null +++ b/data/music/Nuno Adelaida - Amentia/LICENSE.txt @@ -0,0 +1,4 @@ +The songs in this album are licensed under Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) +Artist: https://freemusicarchive.org/music/Nuno_Adelaida +Album: https://freemusicarchive.org/music/Nuno_Adelaida/none_given_1662 +License: https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/data/sounds/accelerate.wav b/data/sounds/accelerate.wav new file mode 100644 index 0000000..026ea50 Binary files /dev/null and b/data/sounds/accelerate.wav differ diff --git a/data/sounds/click.wav b/data/sounds/click.wav new file mode 100644 index 0000000..67e627e Binary files /dev/null and b/data/sounds/click.wav differ diff --git a/data/sounds/click2.wav b/data/sounds/click2.wav new file mode 100644 index 0000000..a92759a Binary files /dev/null and b/data/sounds/click2.wav differ diff --git a/data/sounds/click3.wav b/data/sounds/click3.wav new file mode 100644 index 0000000..b49ff1e Binary files /dev/null and b/data/sounds/click3.wav differ diff --git a/data/sounds/event.wav b/data/sounds/event.wav new file mode 100644 index 0000000..a691fe6 Binary files /dev/null and b/data/sounds/event.wav differ diff --git a/data/sounds/return.wav b/data/sounds/return.wav new file mode 100644 index 0000000..749161e Binary files /dev/null and b/data/sounds/return.wav differ diff --git a/data/world/boat-routes.json b/data/world/boat-routes.json new file mode 100644 index 0000000..96b5688 --- /dev/null +++ b/data/world/boat-routes.json @@ -0,0 +1,202 @@ +[ + [ + 0.479049, 0.271413, + 0.475922, 0.271413, + 0.468418, 0.271413, + 0.456535, 0.276974, + 0.442777, 0.289210, + 0.434021, 0.300334, + 0.422139, 0.317019, + 0.412758, 0.335929, + 0.403377, 0.353726, + 0.395872, 0.368187, + 0.384615, 0.387097, + 0.374609, 0.404894, + 0.367104, 0.419355, + 0.357098, 0.430478, + 0.348968, 0.436040, + 0.343965, 0.438265, + 0.338962, 0.438265, + 0.332083, 0.438265, + 0.321451, 0.438265, + 0.313946, 0.430478, + 0.307692, 0.427141, + 0.301438, 0.423804, + 0.290807, 0.423804, + 0.284553, 0.424917, + 0.275797, 0.434928, + 0.260788, 0.462736, + 0.240775, 0.506118, + 0.222639, 0.540601, + 0.197624, 0.561735, + 0.165103, 0.567297, + 0.131957, 0.570634, + 0.098812, 0.570634, + 0.072545, 0.570634, + 0.052533, 0.570634, + 0.037523, 0.570634, + 0.025016, 0.568409, + 0.015635, 0.568409, + 0.005629, 0.567297, + 0.000000, 0.567297, -0.0500000, 0.567297 + ], + [ + 0.516847, 0.213559, + 0.515575, 0.211299, + 0.514940, 0.210169, + 0.512397, 0.207910, + 0.510490, 0.207910, + 0.509218, 0.207910, + 0.508582, 0.207910, + 0.507947, 0.210169, + 0.507947, 0.210169, + 0.507947, 0.211299, + 0.507947, 0.211299 + ], + [ + 0.915592, 0.707292, + 0.915592, 0.707292, + 0.915592, 0.707292, + 0.915006, 0.709375, + 0.915006, 0.709375, + 0.913833, 0.712500, + 0.912075, 0.714583, + 0.909144, 0.716667, + 0.907386, 0.717708, + 0.904455, 0.720833, + 0.901524, 0.722917, + 0.896249, 0.725000, + 0.890973, 0.726042, + 0.885698, 0.727083, + 0.881594, 0.727083, + 0.877491, 0.727083, + 0.873974, 0.727083, + 0.868699, 0.727083, + 0.861665, 0.727083, + 0.855217, 0.727083, + 0.850528, 0.727083, + 0.847011, 0.727083, + 0.842907, 0.726042, + 0.838804, 0.725000, + 0.835287, 0.722917, + 0.830012, 0.720833, + 0.824736, 0.718750, + 0.818875, 0.712500, + 0.813013, 0.706250, + 0.807737, 0.700000, + 0.803048, 0.690625, + 0.798945, 0.684375, + 0.796014, 0.680208, + 0.791911, 0.672917, + 0.789566, 0.667708, + 0.787222, 0.661458, + 0.783705, 0.656250, + 0.781360, 0.650000, + 0.777843, 0.643750, + 0.774912, 0.634375, + 0.773154, 0.628125, + 0.770809, 0.623958, + 0.766120, 0.617708, + 0.762603, 0.608333, + 0.759672, 0.602083, + 0.756741, 0.592708, + 0.754396, 0.588542, + 0.751465, 0.580208, + 0.749121, 0.575000, + 0.745604, 0.569792, + 0.742087, 0.562500, + 0.738570, 0.555208, + 0.735053, 0.550000, + 0.732122, 0.544792, + 0.728019, 0.536458, + 0.727433, 0.535417, + 0.727433, 0.535417, + 0.727433, 0.535417, + 0.727433, 0.535417, + 0.727433, 0.535417, + 0.726260, 0.533333, + 0.722743, 0.519792, + 0.720985, 0.512500, + 0.718640, 0.506250, + 0.713951, 0.496875, + 0.708089, 0.484375, + 0.699883, 0.470833, + 0.691676, 0.458333, + 0.684056, 0.445833, + 0.679953, 0.439583, + 0.677022, 0.435417, + 0.671747, 0.429167, + 0.665299, 0.427083, + 0.660610, 0.426042, + 0.658265, 0.426042, + 0.655334, 0.426042, + 0.650059, 0.426042, + 0.643611, 0.427083, + 0.640094, 0.428125, + 0.637163, 0.428125, + 0.636577, 0.428125, + 0.635404, 0.429167, + 0.633646, 0.430208, + 0.630129, 0.430208, + 0.628957, 0.428125, + 0.626612, 0.423958, + 0.623095, 0.417708, + 0.620750, 0.411458, + 0.618406, 0.403125, + 0.617819, 0.402083, + 0.617233, 0.395833, + 0.615475, 0.389583, + 0.615475, 0.389583, + 0.615475, 0.389583, + 0.615475, 0.389583, + 0.615475, 0.389583, + 0.615475, 0.384375, + 0.615475, 0.379167, + 0.615475, 0.377083, + 0.613716, 0.377083, + 0.613130, 0.377083, + 0.613130, 0.376042, + 0.610785, 0.369792, + 0.610199, 0.365625, + 0.609613, 0.363542, + 0.608441, 0.357292, + 0.606096, 0.351042, + 0.604338, 0.347917, + 0.604338, 0.345833, + 0.604338, 0.339583, + 0.604338, 0.337500, + 0.604924, 0.332292, + 0.605510, 0.331250, + 0.605510, 0.330208, + 0.605510, 0.330208, + 0.605510, 0.328125, + 0.605510, 0.327083, + 0.603751, 0.321875, + 0.600234, 0.317708, + 0.596717, 0.316667, + 0.594959, 0.316667, + 0.590856, 0.315625, + 0.586167, 0.315625, + 0.582649, 0.313542, + 0.579132, 0.312500, + 0.576202, 0.311458, + 0.574443, 0.310417, + 0.572098, 0.310417, + 0.568581, 0.309375, + 0.565064, 0.308333, + 0.563306, 0.307292, + 0.562720, 0.306250, + 0.560375, 0.300000, + 0.560375, 0.298958, + 0.560375, 0.293750, + 0.559789, 0.290625, + 0.559789, 0.287500, + 0.560375, 0.284375, + 0.560375, 0.283333, + 0.562134, 0.282292, + 0.562134, 0.282292, + 0.562134, 0.282292, + 0.562134, 0.282292, + 0.562134, 0.282292 + ] +] \ No newline at end of file diff --git a/data/world/companies.json b/data/world/companies.json new file mode 100644 index 0000000..65dd232 --- /dev/null +++ b/data/world/companies.json @@ -0,0 +1,52 @@ +[{ + "name": "Fruitosis", + "logo": "data/img/logo_fruitosis.png", + "products": [ + "Bananas", + "Apples", + "Mangos", + "Kiwifruit", + "Kumquat", + "Lemon", + "Lime", + "Loganberry", + "Pineapple", + "Pineberry", + "Plumcot", + "Pomegranate", + "Durian", + "Egg Fruit", + "Elderberry" + ] + }, + { + "name": "Chop Inc", + "logo": "data/img/logo_fruitosis.png", + "products": [ + "Beech logs", + "Ash logs", + "Ginkgo logs", + "Honey Locust logs", + "Walnut logs", + "Alders logs", + "Snowy Mespils logs", + "Birches logs", + "Hornbeams logs" + ] + }, + { + "name": "Chomed Co", + "logo": "data/img/logo_fruitosis.png", + "products": [ + "Lisinopril", + "Atorvastatin", + "Levothyroxine", + "Metformin", + "Amlodipine", + "Metoprolol", + "Omeprazole", + "Simvastatin", + "Losartan" + ] + } +] \ No newline at end of file diff --git a/data/world/dealers.json b/data/world/dealers.json new file mode 100644 index 0000000..714972a --- /dev/null +++ b/data/world/dealers.json @@ -0,0 +1,79 @@ +[{ + "name": "Mercedes-Benz", + "logo": "data/img/mercedes-logo.png", + "trucks": [{ + "name": "Actros", + "power": 320, + "price": 110000, + "fuelcapacity": 600, + "torque": 1080, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + }, + { + "name": "New Actros", + "power": 517, + "price": 220000, + "fuelcapacity": 960, + "torque": 1100, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + } + ] + }, + { + "name": "Iveco", + "logo": "data/img/iveco-logo.png", + "trucks": [{ + "name": "Stralis Active Space", + "power": 310, + "price": 90000, + "fuelcapacity": 1200, + "torque": 1340, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + }, + { + "name": "Stralis Large Space", + "power": 310, + "price": 90000, + "fuelcapacity": 1200, + "torque": 1340, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + }, + { + "name": "Stralis Hi-Way", + "power": 310, + "price": 155000, + "fuelcapacity": 1200, + "torque": 1400, + "logo": "data/img/iveco-stralis-hiway.png", + "fuelusage": 30.0 + } + ] + }, + { + "name": "Volvo", + "logo": "data/img/volvo-logo.png", + "trucks": [{ + "name": "FH Sleeper", + "power": 420, + "price": 112000, + "fuelcapacity": 1400, + "torque": 1000, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + }, + { + "name": "FH Globetrotter", + "power": 500, + "price": 170000, + "fuelcapacity": 800, + "torque": 1150, + "logo": "data/img/iveco-stralis-activespace.png", + "fuelusage": 30.0 + } + ] + } +] \ No newline at end of file diff --git a/data/world/first-names.json b/data/world/first-names.json new file mode 100644 index 0000000..6f961a4 --- /dev/null +++ b/data/world/first-names.json @@ -0,0 +1,4948 @@ +[ + "Aaren", + "Aarika", + "Abagael", + "Abagail", + "Abbe", + "Abbey", + "Abbi", + "Abbie", + "Abby", + "Abbye", + "Abigael", + "Abigail", + "Abigale", + "Abra", + "Ada", + "Adah", + "Adaline", + "Adan", + "Adara", + "Adda", + "Addi", + "Addia", + "Addie", + "Addy", + "Adel", + "Adela", + "Adelaida", + "Adelaide", + "Adele", + "Adelheid", + "Adelice", + "Adelina", + "Adelind", + "Adeline", + "Adella", + "Adelle", + "Adena", + "Adey", + "Adi", + "Adiana", + "Adina", + "Adora", + "Adore", + "Adoree", + "Adorne", + "Adrea", + "Adria", + "Adriaens", + "Adrian", + "Adriana", + "Adriane", + "Adrianna", + "Adrianne", + "Adriena", + "Adrienne", + "Aeriel", + "Aeriela", + "Aeriell", + "Afton", + "Ag", + "Agace", + "Agata", + "Agatha", + "Agathe", + "Aggi", + "Aggie", + "Aggy", + "Agna", + "Agnella", + "Agnes", + "Agnese", + "Agnesse", + "Agneta", + "Agnola", + "Agretha", + "Aida", + "Aidan", + "Aigneis", + "Aila", + "Aile", + "Ailee", + "Aileen", + "Ailene", + "Ailey", + "Aili", + "Ailina", + "Ailis", + "Ailsun", + "Ailyn", + "Aime", + "Aimee", + "Aimil", + "Aindrea", + "Ainslee", + "Ainsley", + "Ainslie", + "Ajay", + "Alaine", + "Alameda", + "Alana", + "Alanah", + "Alane", + "Alanna", + "Alayne", + "Alberta", + "Albertina", + "Albertine", + "Albina", + "Alecia", + "Aleda", + "Aleece", + "Aleen", + "Alejandra", + "Alejandrina", + "Alena", + "Alene", + "Alessandra", + "Aleta", + "Alethea", + "Alex", + "Alexa", + "Alexandra", + "Alexandrina", + "Alexi", + "Alexia", + "Alexina", + "Alexine", + "Alexis", + "Alfi", + "Alfie", + "Alfreda", + "Alfy", + "Ali", + "Alia", + "Alica", + "Alice", + "Alicea", + "Alicia", + "Alida", + "Alidia", + "Alie", + "Alika", + "Alikee", + "Alina", + "Aline", + "Alis", + "Alisa", + "Alisha", + "Alison", + "Alissa", + "Alisun", + "Alix", + "Aliza", + "Alla", + "Alleen", + "Allegra", + "Allene", + "Alli", + "Allianora", + "Allie", + "Allina", + "Allis", + "Allison", + "Allissa", + "Allix", + "Allsun", + "Allx", + "Ally", + "Allyce", + "Allyn", + "Allys", + "Allyson", + "Alma", + "Almeda", + "Almeria", + "Almeta", + "Almira", + "Almire", + "Aloise", + "Aloisia", + "Aloysia", + "Alta", + "Althea", + "Alvera", + "Alverta", + "Alvina", + "Alvinia", + "Alvira", + "Alyce", + "Alyda", + "Alys", + "Alysa", + "Alyse", + "Alysia", + "Alyson", + "Alyss", + "Alyssa", + "Amabel", + "Amabelle", + "Amalea", + "Amalee", + "Amaleta", + "Amalia", + "Amalie", + "Amalita", + "Amalle", + "Amanda", + "Amandi", + "Amandie", + "Amandy", + "Amara", + "Amargo", + "Amata", + "Amber", + "Amberly", + "Ambur", + "Ame", + "Amelia", + "Amelie", + "Amelina", + "Ameline", + "Amelita", + "Ami", + "Amie", + "Amii", + "Amil", + "Amitie", + "Amity", + "Ammamaria", + "Amy", + "Amye", + "Ana", + "Anabal", + "Anabel", + "Anabella", + "Anabelle", + "Analiese", + "Analise", + "Anallese", + "Anallise", + "Anastasia", + "Anastasie", + "Anastassia", + "Anatola", + "Andee", + "Andeee", + "Anderea", + "Andi", + "Andie", + "Andra", + "Andrea", + "Andreana", + "Andree", + "Andrei", + "Andria", + "Andriana", + "Andriette", + "Andromache", + "Andy", + "Anestassia", + "Anet", + "Anett", + "Anetta", + "Anette", + "Ange", + "Angel", + "Angela", + "Angele", + "Angelia", + "Angelica", + "Angelika", + "Angelina", + "Angeline", + "Angelique", + "Angelita", + "Angelle", + "Angie", + "Angil", + "Angy", + "Ania", + "Anica", + "Anissa", + "Anita", + "Anitra", + "Anjanette", + "Anjela", + "Ann", + "Ann-Marie", + "Anna", + "Anna-Diana", + "Anna-Diane", + "Anna-Maria", + "Annabal", + "Annabel", + "Annabela", + "Annabell", + "Annabella", + "Annabelle", + "Annadiana", + "Annadiane", + "Annalee", + "Annaliese", + "Annalise", + "Annamaria", + "Annamarie", + "Anne", + "Anne-Corinne", + "Anne-Marie", + "Annecorinne", + "Anneliese", + "Annelise", + "Annemarie", + "Annetta", + "Annette", + "Anni", + "Annice", + "Annie", + "Annis", + "Annissa", + "Annmaria", + "Annmarie", + "Annnora", + "Annora", + "Anny", + "Anselma", + "Ansley", + "Anstice", + "Anthe", + "Anthea", + "Anthia", + "Anthiathia", + "Antoinette", + "Antonella", + "Antonetta", + "Antonia", + "Antonie", + "Antonietta", + "Antonina", + "Anya", + "Appolonia", + "April", + "Aprilette", + "Ara", + "Arabel", + "Arabela", + "Arabele", + "Arabella", + "Arabelle", + "Arda", + "Ardath", + "Ardeen", + "Ardelia", + "Ardelis", + "Ardella", + "Ardelle", + "Arden", + "Ardene", + "Ardenia", + "Ardine", + "Ardis", + "Ardisj", + "Ardith", + "Ardra", + "Ardyce", + "Ardys", + "Ardyth", + "Aretha", + "Ariadne", + "Ariana", + "Aridatha", + "Ariel", + "Ariela", + "Ariella", + "Arielle", + "Arlana", + "Arlee", + "Arleen", + "Arlen", + "Arlena", + "Arlene", + "Arleta", + "Arlette", + "Arleyne", + "Arlie", + "Arliene", + "Arlina", + "Arlinda", + "Arline", + "Arluene", + "Arly", + "Arlyn", + "Arlyne", + "Aryn", + "Ashely", + "Ashia", + "Ashien", + "Ashil", + "Ashla", + "Ashlan", + "Ashlee", + "Ashleigh", + "Ashlen", + "Ashley", + "Ashli", + "Ashlie", + "Ashly", + "Asia", + "Astra", + "Astrid", + "Astrix", + "Atalanta", + "Athena", + "Athene", + "Atlanta", + "Atlante", + "Auberta", + "Aubine", + "Aubree", + "Aubrette", + "Aubrey", + "Aubrie", + "Aubry", + "Audi", + "Audie", + "Audra", + "Audre", + "Audrey", + "Audrie", + "Audry", + "Audrye", + "Audy", + "Augusta", + "Auguste", + "Augustina", + "Augustine", + "Aundrea", + "Aura", + "Aurea", + "Aurel", + "Aurelea", + "Aurelia", + "Aurelie", + "Auria", + "Aurie", + "Aurilia", + "Aurlie", + "Auroora", + "Aurora", + "Aurore", + "Austin", + "Austina", + "Austine", + "Ava", + "Aveline", + "Averil", + "Averyl", + "Avie", + "Avis", + "Aviva", + "Avivah", + "Avril", + "Avrit", + "Ayn", + "Bab", + "Babara", + "Babb", + "Babbette", + "Babbie", + "Babette", + "Babita", + "Babs", + "Bambi", + "Bambie", + "Bamby", + "Barb", + "Barbabra", + "Barbara", + "Barbara-Anne", + "Barbaraanne", + "Barbe", + "Barbee", + "Barbette", + "Barbey", + "Barbi", + "Barbie", + "Barbra", + "Barby", + "Bari", + "Barrie", + "Barry", + "Basia", + "Bathsheba", + "Batsheva", + "Bea", + "Beatrice", + "Beatrisa", + "Beatrix", + "Beatriz", + "Bebe", + "Becca", + "Becka", + "Becki", + "Beckie", + "Becky", + "Bee", + "Beilul", + "Beitris", + "Bekki", + "Bel", + "Belia", + "Belicia", + "Belinda", + "Belita", + "Bell", + "Bella", + "Bellanca", + "Belle", + "Bellina", + "Belva", + "Belvia", + "Bendite", + "Benedetta", + "Benedicta", + "Benedikta", + "Benetta", + "Benita", + "Benni", + "Bennie", + "Benny", + "Benoite", + "Berenice", + "Beret", + "Berget", + "Berna", + "Bernadene", + "Bernadette", + "Bernadina", + "Bernadine", + "Bernardina", + "Bernardine", + "Bernelle", + "Bernete", + "Bernetta", + "Bernette", + "Berni", + "Bernice", + "Bernie", + "Bernita", + "Berny", + "Berri", + "Berrie", + "Berry", + "Bert", + "Berta", + "Berte", + "Bertha", + "Berthe", + "Berti", + "Bertie", + "Bertina", + "Bertine", + "Berty", + "Beryl", + "Beryle", + "Bess", + "Bessie", + "Bessy", + "Beth", + "Bethanne", + "Bethany", + "Bethena", + "Bethina", + "Betsey", + "Betsy", + "Betta", + "Bette", + "Bette-Ann", + "Betteann", + "Betteanne", + "Betti", + "Bettina", + "Bettine", + "Betty", + "Bettye", + "Beulah", + "Bev", + "Beverie", + "Beverlee", + "Beverley", + "Beverlie", + "Beverly", + "Bevvy", + "Bianca", + "Bianka", + "Bibbie", + "Bibby", + "Bibbye", + "Bibi", + "Biddie", + "Biddy", + "Bidget", + "Bili", + "Bill", + "Billi", + "Billie", + "Billy", + "Billye", + "Binni", + "Binnie", + "Binny", + "Bird", + "Birdie", + "Birgit", + "Birgitta", + "Blair", + "Blaire", + "Blake", + "Blakelee", + "Blakeley", + "Blanca", + "Blanch", + "Blancha", + "Blanche", + "Blinni", + "Blinnie", + "Blinny", + "Bliss", + "Blisse", + "Blithe", + "Blondell", + "Blondelle", + "Blondie", + "Blondy", + "Blythe", + "Bobbe", + "Bobbee", + "Bobbette", + "Bobbi", + "Bobbie", + "Bobby", + "Bobbye", + "Bobette", + "Bobina", + "Bobine", + "Bobinette", + "Bonita", + "Bonnee", + "Bonni", + "Bonnibelle", + "Bonnie", + "Bonny", + "Brana", + "Brandais", + "Brande", + "Brandea", + "Brandi", + "Brandice", + "Brandie", + "Brandise", + "Brandy", + "Breanne", + "Brear", + "Bree", + "Breena", + "Bren", + "Brena", + "Brenda", + "Brenn", + "Brenna", + "Brett", + "Bria", + "Briana", + "Brianna", + "Brianne", + "Bride", + "Bridget", + "Bridgette", + "Bridie", + "Brier", + "Brietta", + "Brigid", + "Brigida", + "Brigit", + "Brigitta", + "Brigitte", + "Brina", + "Briney", + "Brinn", + "Brinna", + "Briny", + "Brit", + "Brita", + "Britney", + "Britni", + "Britt", + "Britta", + "Brittan", + "Brittaney", + "Brittani", + "Brittany", + "Britte", + "Britteny", + "Brittne", + "Brittney", + "Brittni", + "Brook", + "Brooke", + "Brooks", + "Brunhilda", + "Brunhilde", + "Bryana", + "Bryn", + "Bryna", + "Brynn", + "Brynna", + "Brynne", + "Buffy", + "Bunni", + "Bunnie", + "Bunny", + "Cacilia", + "Cacilie", + "Cahra", + "Cairistiona", + "Caitlin", + "Caitrin", + "Cal", + "Calida", + "Calla", + "Calley", + "Calli", + "Callida", + "Callie", + "Cally", + "Calypso", + "Cam", + "Camala", + "Camel", + "Camella", + "Camellia", + "Cami", + "Camila", + "Camile", + "Camilla", + "Camille", + "Cammi", + "Cammie", + "Cammy", + "Candace", + "Candi", + "Candice", + "Candida", + "Candide", + "Candie", + "Candis", + "Candra", + "Candy", + "Caprice", + "Cara", + "Caralie", + "Caren", + "Carena", + "Caresa", + "Caressa", + "Caresse", + "Carey", + "Cari", + "Caria", + "Carie", + "Caril", + "Carilyn", + "Carin", + "Carina", + "Carine", + "Cariotta", + "Carissa", + "Carita", + "Caritta", + "Carla", + "Carlee", + "Carleen", + "Carlen", + "Carlene", + "Carley", + "Carlie", + "Carlin", + "Carlina", + "Carline", + "Carlita", + "Carlota", + "Carlotta", + "Carly", + "Carlye", + "Carlyn", + "Carlynn", + "Carlynne", + "Carma", + "Carmel", + "Carmela", + "Carmelia", + "Carmelina", + "Carmelita", + "Carmella", + "Carmelle", + "Carmen", + "Carmencita", + "Carmina", + "Carmine", + "Carmita", + "Carmon", + "Caro", + "Carol", + "Carol-Jean", + "Carola", + "Carolan", + "Carolann", + "Carole", + "Carolee", + "Carolin", + "Carolina", + "Caroline", + "Caroljean", + "Carolyn", + "Carolyne", + "Carolynn", + "Caron", + "Carree", + "Carri", + "Carrie", + "Carrissa", + "Carroll", + "Carry", + "Cary", + "Caryl", + "Caryn", + "Casandra", + "Casey", + "Casi", + "Casie", + "Cass", + "Cassandra", + "Cassandre", + "Cassandry", + "Cassaundra", + "Cassey", + "Cassi", + "Cassie", + "Cassondra", + "Cassy", + "Catarina", + "Cate", + "Caterina", + "Catha", + "Catharina", + "Catharine", + "Cathe", + "Cathee", + "Catherin", + "Catherina", + "Catherine", + "Cathi", + "Cathie", + "Cathleen", + "Cathlene", + "Cathrin", + "Cathrine", + "Cathryn", + "Cathy", + "Cathyleen", + "Cati", + "Catie", + "Catina", + "Catlaina", + "Catlee", + "Catlin", + "Catrina", + "Catriona", + "Caty", + "Caye", + "Cayla", + "Cecelia", + "Cecil", + "Cecile", + "Ceciley", + "Cecilia", + "Cecilla", + "Cecily", + "Ceil", + "Cele", + "Celene", + "Celesta", + "Celeste", + "Celestia", + "Celestina", + "Celestine", + "Celestyn", + "Celestyna", + "Celia", + "Celie", + "Celina", + "Celinda", + "Celine", + "Celinka", + "Celisse", + "Celka", + "Celle", + "Cesya", + "Chad", + "Chanda", + "Chandal", + "Chandra", + "Channa", + "Chantal", + "Chantalle", + "Charil", + "Charin", + "Charis", + "Charissa", + "Charisse", + "Charita", + "Charity", + "Charla", + "Charlean", + "Charleen", + "Charlena", + "Charlene", + "Charline", + "Charlot", + "Charlotta", + "Charlotte", + "Charmain", + "Charmaine", + "Charmane", + "Charmian", + "Charmine", + "Charmion", + "Charo", + "Charyl", + "Chastity", + "Chelsae", + "Chelsea", + "Chelsey", + "Chelsie", + "Chelsy", + "Cher", + "Chere", + "Cherey", + "Cheri", + "Cherianne", + "Cherice", + "Cherida", + "Cherie", + "Cherilyn", + "Cherilynn", + "Cherin", + "Cherise", + "Cherish", + "Cherlyn", + "Cherri", + "Cherrita", + "Cherry", + "Chery", + "Cherye", + "Cheryl", + "Cheslie", + "Chiarra", + "Chickie", + "Chicky", + "Chiquia", + "Chiquita", + "Chlo", + "Chloe", + "Chloette", + "Chloris", + "Chris", + "Chrissie", + "Chrissy", + "Christa", + "Christabel", + "Christabella", + "Christal", + "Christalle", + "Christan", + "Christean", + "Christel", + "Christen", + "Christi", + "Christian", + "Christiana", + "Christiane", + "Christie", + "Christin", + "Christina", + "Christine", + "Christy", + "Christye", + "Christyna", + "Chrysa", + "Chrysler", + "Chrystal", + "Chryste", + "Chrystel", + "Cicely", + "Cicily", + "Ciel", + "Cilka", + "Cinda", + "Cindee", + "Cindelyn", + "Cinderella", + "Cindi", + "Cindie", + "Cindra", + "Cindy", + "Cinnamon", + "Cissiee", + "Cissy", + "Clair", + "Claire", + "Clara", + "Clarabelle", + "Clare", + "Claresta", + "Clareta", + "Claretta", + "Clarette", + "Clarey", + "Clari", + "Claribel", + "Clarice", + "Clarie", + "Clarinda", + "Clarine", + "Clarissa", + "Clarisse", + "Clarita", + "Clary", + "Claude", + "Claudelle", + "Claudetta", + "Claudette", + "Claudia", + "Claudie", + "Claudina", + "Claudine", + "Clea", + "Clem", + "Clemence", + "Clementia", + "Clementina", + "Clementine", + "Clemmie", + "Clemmy", + "Cleo", + "Cleopatra", + "Clerissa", + "Clio", + "Clo", + "Cloe", + "Cloris", + "Clotilda", + "Clovis", + "Codee", + "Codi", + "Codie", + "Cody", + "Coleen", + "Colene", + "Coletta", + "Colette", + "Colleen", + "Collen", + "Collete", + "Collette", + "Collie", + "Colline", + "Colly", + "Con", + "Concettina", + "Conchita", + "Concordia", + "Conni", + "Connie", + "Conny", + "Consolata", + "Constance", + "Constancia", + "Constancy", + "Constanta", + "Constantia", + "Constantina", + "Constantine", + "Consuela", + "Consuelo", + "Cookie", + "Cora", + "Corabel", + "Corabella", + "Corabelle", + "Coral", + "Coralie", + "Coraline", + "Coralyn", + "Cordelia", + "Cordelie", + "Cordey", + "Cordi", + "Cordie", + "Cordula", + "Cordy", + "Coreen", + "Corella", + "Corenda", + "Corene", + "Coretta", + "Corette", + "Corey", + "Cori", + "Corie", + "Corilla", + "Corina", + "Corine", + "Corinna", + "Corinne", + "Coriss", + "Corissa", + "Corliss", + "Corly", + "Cornela", + "Cornelia", + "Cornelle", + "Cornie", + "Corny", + "Correna", + "Correy", + "Corri", + "Corrianne", + "Corrie", + "Corrina", + "Corrine", + "Corrinne", + "Corry", + "Cortney", + "Cory", + "Cosetta", + "Cosette", + "Costanza", + "Courtenay", + "Courtnay", + "Courtney", + "Crin", + "Cris", + "Crissie", + "Crissy", + "Crista", + "Cristabel", + "Cristal", + "Cristen", + "Cristi", + "Cristie", + "Cristin", + "Cristina", + "Cristine", + "Cristionna", + "Cristy", + "Crysta", + "Crystal", + "Crystie", + "Cthrine", + "Cyb", + "Cybil", + "Cybill", + "Cymbre", + "Cynde", + "Cyndi", + "Cyndia", + "Cyndie", + "Cyndy", + "Cynthea", + "Cynthia", + "Cynthie", + "Cynthy", + "Dacey", + "Dacia", + "Dacie", + "Dacy", + "Dael", + "Daffi", + "Daffie", + "Daffy", + "Dagmar", + "Dahlia", + "Daile", + "Daisey", + "Daisi", + "Daisie", + "Daisy", + "Dale", + "Dalenna", + "Dalia", + "Dalila", + "Dallas", + "Daloris", + "Damara", + "Damaris", + "Damita", + "Dana", + "Danell", + "Danella", + "Danette", + "Dani", + "Dania", + "Danica", + "Danice", + "Daniela", + "Daniele", + "Daniella", + "Danielle", + "Danika", + "Danila", + "Danit", + "Danita", + "Danna", + "Danni", + "Dannie", + "Danny", + "Dannye", + "Danya", + "Danyelle", + "Danyette", + "Daphene", + "Daphna", + "Daphne", + "Dara", + "Darb", + "Darbie", + "Darby", + "Darcee", + "Darcey", + "Darci", + "Darcie", + "Darcy", + "Darda", + "Dareen", + "Darell", + "Darelle", + "Dari", + "Daria", + "Darice", + "Darla", + "Darleen", + "Darlene", + "Darline", + "Darlleen", + "Daron", + "Darrelle", + "Darryl", + "Darsey", + "Darsie", + "Darya", + "Daryl", + "Daryn", + "Dasha", + "Dasi", + "Dasie", + "Dasya", + "Datha", + "Daune", + "Daveen", + "Daveta", + "Davida", + "Davina", + "Davine", + "Davita", + "Dawn", + "Dawna", + "Dayle", + "Dayna", + "Ddene", + "De", + "Deana", + "Deane", + "Deanna", + "Deanne", + "Deb", + "Debbi", + "Debbie", + "Debby", + "Debee", + "Debera", + "Debi", + "Debor", + "Debora", + "Deborah", + "Debra", + "Dede", + "Dedie", + "Dedra", + "Dee", + "Dee Dee", + "Deeann", + "Deeanne", + "Deedee", + "Deena", + "Deerdre", + "Deeyn", + "Dehlia", + "Deidre", + "Deina", + "Deirdre", + "Del", + "Dela", + "Delcina", + "Delcine", + "Delia", + "Delila", + "Delilah", + "Delinda", + "Dell", + "Della", + "Delly", + "Delora", + "Delores", + "Deloria", + "Deloris", + "Delphine", + "Delphinia", + "Demeter", + "Demetra", + "Demetria", + "Demetris", + "Dena", + "Deni", + "Denice", + "Denise", + "Denna", + "Denni", + "Dennie", + "Denny", + "Deny", + "Denys", + "Denyse", + "Deonne", + "Desdemona", + "Desirae", + "Desiree", + "Desiri", + "Deva", + "Devan", + "Devi", + "Devin", + "Devina", + "Devinne", + "Devon", + "Devondra", + "Devonna", + "Devonne", + "Devora", + "Di", + "Diahann", + "Dian", + "Diana", + "Diandra", + "Diane", + "Diane-Marie", + "Dianemarie", + "Diann", + "Dianna", + "Dianne", + "Diannne", + "Didi", + "Dido", + "Diena", + "Dierdre", + "Dina", + "Dinah", + "Dinnie", + "Dinny", + "Dion", + "Dione", + "Dionis", + "Dionne", + "Dita", + "Dix", + "Dixie", + "Dniren", + "Dode", + "Dodi", + "Dodie", + "Dody", + "Doe", + "Doll", + "Dolley", + "Dolli", + "Dollie", + "Dolly", + "Dolores", + "Dolorita", + "Doloritas", + "Domeniga", + "Dominga", + "Domini", + "Dominica", + "Dominique", + "Dona", + "Donella", + "Donelle", + "Donetta", + "Donia", + "Donica", + "Donielle", + "Donna", + "Donnamarie", + "Donni", + "Donnie", + "Donny", + "Dora", + "Doralia", + "Doralin", + "Doralyn", + "Doralynn", + "Doralynne", + "Dore", + "Doreen", + "Dorelia", + "Dorella", + "Dorelle", + "Dorena", + "Dorene", + "Doretta", + "Dorette", + "Dorey", + "Dori", + "Doria", + "Dorian", + "Dorice", + "Dorie", + "Dorine", + "Doris", + "Dorisa", + "Dorise", + "Dorita", + "Doro", + "Dorolice", + "Dorolisa", + "Dorotea", + "Doroteya", + "Dorothea", + "Dorothee", + "Dorothy", + "Dorree", + "Dorri", + "Dorrie", + "Dorris", + "Dorry", + "Dorthea", + "Dorthy", + "Dory", + "Dosi", + "Dot", + "Doti", + "Dotti", + "Dottie", + "Dotty", + "Dre", + "Dreddy", + "Dredi", + "Drona", + "Dru", + "Druci", + "Drucie", + "Drucill", + "Drucy", + "Drusi", + "Drusie", + "Drusilla", + "Drusy", + "Dulce", + "Dulcea", + "Dulci", + "Dulcia", + "Dulciana", + "Dulcie", + "Dulcine", + "Dulcinea", + "Dulcy", + "Dulsea", + "Dusty", + "Dyan", + "Dyana", + "Dyane", + "Dyann", + "Dyanna", + "Dyanne", + "Dyna", + "Dynah", + "Eachelle", + "Eada", + "Eadie", + "Eadith", + "Ealasaid", + "Eartha", + "Easter", + "Eba", + "Ebba", + "Ebonee", + "Ebony", + "Eda", + "Eddi", + "Eddie", + "Eddy", + "Ede", + "Edee", + "Edeline", + "Eden", + "Edi", + "Edie", + "Edin", + "Edita", + "Edith", + "Editha", + "Edithe", + "Ediva", + "Edna", + "Edwina", + "Edy", + "Edyth", + "Edythe", + "Effie", + "Eileen", + "Eilis", + "Eimile", + "Eirena", + "Ekaterina", + "Elaina", + "Elaine", + "Elana", + "Elane", + "Elayne", + "Elberta", + "Elbertina", + "Elbertine", + "Eleanor", + "Eleanora", + "Eleanore", + "Electra", + "Eleen", + "Elena", + "Elene", + "Eleni", + "Elenore", + "Eleonora", + "Eleonore", + "Elfie", + "Elfreda", + "Elfrida", + "Elfrieda", + "Elga", + "Elianora", + "Elianore", + "Elicia", + "Elie", + "Elinor", + "Elinore", + "Elisa", + "Elisabet", + "Elisabeth", + "Elisabetta", + "Elise", + "Elisha", + "Elissa", + "Elita", + "Eliza", + "Elizabet", + "Elizabeth", + "Elka", + "Elke", + "Ella", + "Elladine", + "Elle", + "Ellen", + "Ellene", + "Ellette", + "Elli", + "Ellie", + "Ellissa", + "Elly", + "Ellyn", + "Ellynn", + "Elmira", + "Elna", + "Elnora", + "Elnore", + "Eloisa", + "Eloise", + "Elonore", + "Elora", + "Elsa", + "Elsbeth", + "Else", + "Elset", + "Elsey", + "Elsi", + "Elsie", + "Elsinore", + "Elspeth", + "Elsy", + "Elva", + "Elvera", + "Elvina", + "Elvira", + "Elwira", + "Elyn", + "Elyse", + "Elysee", + "Elysha", + "Elysia", + "Elyssa", + "Em", + "Ema", + "Emalee", + "Emalia", + "Emelda", + "Emelia", + "Emelina", + "Emeline", + "Emelita", + "Emelyne", + "Emera", + "Emilee", + "Emili", + "Emilia", + "Emilie", + "Emiline", + "Emily", + "Emlyn", + "Emlynn", + "Emlynne", + "Emma", + "Emmalee", + "Emmaline", + "Emmalyn", + "Emmalynn", + "Emmalynne", + "Emmeline", + "Emmey", + "Emmi", + "Emmie", + "Emmy", + "Emmye", + "Emogene", + "Emyle", + "Emylee", + "Engracia", + "Enid", + "Enrica", + "Enrichetta", + "Enrika", + "Enriqueta", + "Eolanda", + "Eolande", + "Eran", + "Erda", + "Erena", + "Erica", + "Ericha", + "Ericka", + "Erika", + "Erin", + "Erina", + "Erinn", + "Erinna", + "Erma", + "Ermengarde", + "Ermentrude", + "Ermina", + "Erminia", + "Erminie", + "Erna", + "Ernaline", + "Ernesta", + "Ernestine", + "Ertha", + "Eryn", + "Esma", + "Esmaria", + "Esme", + "Esmeralda", + "Essa", + "Essie", + "Essy", + "Esta", + "Estel", + "Estele", + "Estell", + "Estella", + "Estelle", + "Ester", + "Esther", + "Estrella", + "Estrellita", + "Ethel", + "Ethelda", + "Ethelin", + "Ethelind", + "Etheline", + "Ethelyn", + "Ethyl", + "Etta", + "Etti", + "Ettie", + "Etty", + "Eudora", + "Eugenia", + "Eugenie", + "Eugine", + "Eula", + "Eulalie", + "Eunice", + "Euphemia", + "Eustacia", + "Eva", + "Evaleen", + "Evangelia", + "Evangelin", + "Evangelina", + "Evangeline", + "Evania", + "Evanne", + "Eve", + "Eveleen", + "Evelina", + "Eveline", + "Evelyn", + "Evey", + "Evie", + "Evita", + "Evonne", + "Evvie", + "Evvy", + "Evy", + "Eyde", + "Eydie", + "Ezmeralda", + "Fae", + "Faina", + "Faith", + "Fallon", + "Fan", + "Fanchette", + "Fanchon", + "Fancie", + "Fancy", + "Fanechka", + "Fania", + "Fanni", + "Fannie", + "Fanny", + "Fanya", + "Fara", + "Farah", + "Farand", + "Farica", + "Farra", + "Farrah", + "Farrand", + "Faun", + "Faunie", + "Faustina", + "Faustine", + "Fawn", + "Fawne", + "Fawnia", + "Fay", + "Faydra", + "Faye", + "Fayette", + "Fayina", + "Fayre", + "Fayth", + "Faythe", + "Federica", + "Fedora", + "Felecia", + "Felicdad", + "Felice", + "Felicia", + "Felicity", + "Felicle", + "Felipa", + "Felisha", + "Felita", + "Feliza", + "Fenelia", + "Feodora", + "Ferdinanda", + "Ferdinande", + "Fern", + "Fernanda", + "Fernande", + "Fernandina", + "Ferne", + "Fey", + "Fiann", + "Fianna", + "Fidela", + "Fidelia", + "Fidelity", + "Fifi", + "Fifine", + "Filia", + "Filide", + "Filippa", + "Fina", + "Fiona", + "Fionna", + "Fionnula", + "Fiorenze", + "Fleur", + "Fleurette", + "Flo", + "Flor", + "Flora", + "Florance", + "Flore", + "Florella", + "Florence", + "Florencia", + "Florentia", + "Florenza", + "Florette", + "Flori", + "Floria", + "Florida", + "Florie", + "Florina", + "Florinda", + "Floris", + "Florri", + "Florrie", + "Florry", + "Flory", + "Flossi", + "Flossie", + "Flossy", + "Flss", + "Fran", + "Francene", + "Frances", + "Francesca", + "Francine", + "Francisca", + "Franciska", + "Francoise", + "Francyne", + "Frank", + "Frankie", + "Franky", + "Franni", + "Frannie", + "Franny", + "Frayda", + "Fred", + "Freda", + "Freddi", + "Freddie", + "Freddy", + "Fredelia", + "Frederica", + "Fredericka", + "Frederique", + "Fredi", + "Fredia", + "Fredra", + "Fredrika", + "Freida", + "Frieda", + "Friederike", + "Fulvia", + "Gabbey", + "Gabbi", + "Gabbie", + "Gabey", + "Gabi", + "Gabie", + "Gabriel", + "Gabriela", + "Gabriell", + "Gabriella", + "Gabrielle", + "Gabriellia", + "Gabrila", + "Gaby", + "Gae", + "Gael", + "Gail", + "Gale", + "Gale", + "Galina", + "Garland", + "Garnet", + "Garnette", + "Gates", + "Gavra", + "Gavrielle", + "Gay", + "Gaye", + "Gayel", + "Gayla", + "Gayle", + "Gayleen", + "Gaylene", + "Gaynor", + "Gelya", + "Gena", + "Gene", + "Geneva", + "Genevieve", + "Genevra", + "Genia", + "Genna", + "Genni", + "Gennie", + "Gennifer", + "Genny", + "Genovera", + "Genvieve", + "George", + "Georgeanna", + "Georgeanne", + "Georgena", + "Georgeta", + "Georgetta", + "Georgette", + "Georgia", + "Georgiana", + "Georgianna", + "Georgianne", + "Georgie", + "Georgina", + "Georgine", + "Geralda", + "Geraldine", + "Gerda", + "Gerhardine", + "Geri", + "Gerianna", + "Gerianne", + "Gerladina", + "Germain", + "Germaine", + "Germana", + "Gerri", + "Gerrie", + "Gerrilee", + "Gerry", + "Gert", + "Gerta", + "Gerti", + "Gertie", + "Gertrud", + "Gertruda", + "Gertrude", + "Gertrudis", + "Gerty", + "Giacinta", + "Giana", + "Gianina", + "Gianna", + "Gigi", + "Gilberta", + "Gilberte", + "Gilbertina", + "Gilbertine", + "Gilda", + "Gilemette", + "Gill", + "Gillan", + "Gilli", + "Gillian", + "Gillie", + "Gilligan", + "Gilly", + "Gina", + "Ginelle", + "Ginevra", + "Ginger", + "Ginni", + "Ginnie", + "Ginnifer", + "Ginny", + "Giorgia", + "Giovanna", + "Gipsy", + "Giralda", + "Gisela", + "Gisele", + "Gisella", + "Giselle", + "Giuditta", + "Giulia", + "Giulietta", + "Giustina", + "Gizela", + "Glad", + "Gladi", + "Gladys", + "Gleda", + "Glen", + "Glenda", + "Glenine", + "Glenn", + "Glenna", + "Glennie", + "Glennis", + "Glori", + "Gloria", + "Gloriana", + "Gloriane", + "Glory", + "Glyn", + "Glynda", + "Glynis", + "Glynnis", + "Gnni", + "Godiva", + "Golda", + "Goldarina", + "Goldi", + "Goldia", + "Goldie", + "Goldina", + "Goldy", + "Grace", + "Gracia", + "Gracie", + "Grata", + "Gratia", + "Gratiana", + "Gray", + "Grayce", + "Grazia", + "Greer", + "Greta", + "Gretal", + "Gretchen", + "Grete", + "Gretel", + "Grethel", + "Gretna", + "Gretta", + "Grier", + "Griselda", + "Grissel", + "Guendolen", + "Guenevere", + "Guenna", + "Guglielma", + "Gui", + "Guillema", + "Guillemette", + "Guinevere", + "Guinna", + "Gunilla", + "Gus", + "Gusella", + "Gussi", + "Gussie", + "Gussy", + "Gusta", + "Gusti", + "Gustie", + "Gusty", + "Gwen", + "Gwendolen", + "Gwendolin", + "Gwendolyn", + "Gweneth", + "Gwenette", + "Gwenneth", + "Gwenni", + "Gwennie", + "Gwenny", + "Gwenora", + "Gwenore", + "Gwyn", + "Gwyneth", + "Gwynne", + "Gypsy", + "Hadria", + "Hailee", + "Haily", + "Haleigh", + "Halette", + "Haley", + "Hali", + "Halie", + "Halimeda", + "Halley", + "Halli", + "Hallie", + "Hally", + "Hana", + "Hanna", + "Hannah", + "Hanni", + "Hannie", + "Hannis", + "Hanny", + "Happy", + "Harlene", + "Harley", + "Harli", + "Harlie", + "Harmonia", + "Harmonie", + "Harmony", + "Harri", + "Harrie", + "Harriet", + "Harriett", + "Harrietta", + "Harriette", + "Harriot", + "Harriott", + "Hatti", + "Hattie", + "Hatty", + "Hayley", + "Hazel", + "Heath", + "Heather", + "Heda", + "Hedda", + "Heddi", + "Heddie", + "Hedi", + "Hedvig", + "Hedvige", + "Hedwig", + "Hedwiga", + "Hedy", + "Heida", + "Heidi", + "Heidie", + "Helaina", + "Helaine", + "Helen", + "Helen-Elizabeth", + "Helena", + "Helene", + "Helenka", + "Helga", + "Helge", + "Helli", + "Heloise", + "Helsa", + "Helyn", + "Hendrika", + "Henka", + "Henrie", + "Henrieta", + "Henrietta", + "Henriette", + "Henryetta", + "Hephzibah", + "Hermia", + "Hermina", + "Hermine", + "Herminia", + "Hermione", + "Herta", + "Hertha", + "Hester", + "Hesther", + "Hestia", + "Hetti", + "Hettie", + "Hetty", + "Hilary", + "Hilda", + "Hildagard", + "Hildagarde", + "Hilde", + "Hildegaard", + "Hildegarde", + "Hildy", + "Hillary", + "Hilliary", + "Hinda", + "Holli", + "Hollie", + "Holly", + "Holly-Anne", + "Hollyanne", + "Honey", + "Honor", + "Honoria", + "Hope", + "Horatia", + "Hortense", + "Hortensia", + "Hulda", + "Hyacinth", + "Hyacintha", + "Hyacinthe", + "Hyacinthia", + "Hyacinthie", + "Hynda", + "Ianthe", + "Ibbie", + "Ibby", + "Ida", + "Idalia", + "Idalina", + "Idaline", + "Idell", + "Idelle", + "Idette", + "Ileana", + "Ileane", + "Ilene", + "Ilise", + "Ilka", + "Illa", + "Ilsa", + "Ilse", + "Ilysa", + "Ilyse", + "Ilyssa", + "Imelda", + "Imogen", + "Imogene", + "Imojean", + "Ina", + "Indira", + "Ines", + "Inesita", + "Inessa", + "Inez", + "Inga", + "Ingaberg", + "Ingaborg", + "Inge", + "Ingeberg", + "Ingeborg", + "Inger", + "Ingrid", + "Ingunna", + "Inna", + "Iolande", + "Iolanthe", + "Iona", + "Iormina", + "Ira", + "Irena", + "Irene", + "Irina", + "Iris", + "Irita", + "Irma", + "Isa", + "Isabel", + "Isabelita", + "Isabella", + "Isabelle", + "Isadora", + "Isahella", + "Iseabal", + "Isidora", + "Isis", + "Isobel", + "Issi", + "Issie", + "Issy", + "Ivett", + "Ivette", + "Ivie", + "Ivonne", + "Ivory", + "Ivy", + "Izabel", + "Jacenta", + "Jacinda", + "Jacinta", + "Jacintha", + "Jacinthe", + "Jackelyn", + "Jacki", + "Jackie", + "Jacklin", + "Jacklyn", + "Jackquelin", + "Jackqueline", + "Jacky", + "Jaclin", + "Jaclyn", + "Jacquelin", + "Jacqueline", + "Jacquelyn", + "Jacquelynn", + "Jacquenetta", + "Jacquenette", + "Jacquetta", + "Jacquette", + "Jacqui", + "Jacquie", + "Jacynth", + "Jada", + "Jade", + "Jaime", + "Jaimie", + "Jaine", + "Jami", + "Jamie", + "Jamima", + "Jammie", + "Jan", + "Jana", + "Janaya", + "Janaye", + "Jandy", + "Jane", + "Janean", + "Janeczka", + "Janeen", + "Janel", + "Janela", + "Janella", + "Janelle", + "Janene", + "Janenna", + "Janessa", + "Janet", + "Janeta", + "Janetta", + "Janette", + "Janeva", + "Janey", + "Jania", + "Janice", + "Janie", + "Janifer", + "Janina", + "Janine", + "Janis", + "Janith", + "Janka", + "Janna", + "Jannel", + "Jannelle", + "Janot", + "Jany", + "Jaquelin", + "Jaquelyn", + "Jaquenetta", + "Jaquenette", + "Jaquith", + "Jasmin", + "Jasmina", + "Jasmine", + "Jayme", + "Jaymee", + "Jayne", + "Jaynell", + "Jazmin", + "Jean", + "Jeana", + "Jeane", + "Jeanelle", + "Jeanette", + "Jeanie", + "Jeanine", + "Jeanna", + "Jeanne", + "Jeannette", + "Jeannie", + "Jeannine", + "Jehanna", + "Jelene", + "Jemie", + "Jemima", + "Jemimah", + "Jemmie", + "Jemmy", + "Jen", + "Jena", + "Jenda", + "Jenelle", + "Jeni", + "Jenica", + "Jeniece", + "Jenifer", + "Jeniffer", + "Jenilee", + "Jenine", + "Jenn", + "Jenna", + "Jennee", + "Jennette", + "Jenni", + "Jennica", + "Jennie", + "Jennifer", + "Jennilee", + "Jennine", + "Jenny", + "Jeralee", + "Jere", + "Jeri", + "Jermaine", + "Jerrie", + "Jerrilee", + "Jerrilyn", + "Jerrine", + "Jerry", + "Jerrylee", + "Jess", + "Jessa", + "Jessalin", + "Jessalyn", + "Jessamine", + "Jessamyn", + "Jesse", + "Jesselyn", + "Jessi", + "Jessica", + "Jessie", + "Jessika", + "Jessy", + "Jewel", + "Jewell", + "Jewelle", + "Jill", + "Jillana", + "Jillane", + "Jillayne", + "Jilleen", + "Jillene", + "Jilli", + "Jillian", + "Jillie", + "Jilly", + "Jinny", + "Jo", + "Jo Ann", + "Jo-Ann", + "Jo-Anne", + "Joan", + "Joana", + "Joane", + "Joanie", + "Joann", + "Joanna", + "Joanne", + "Joannes", + "Jobey", + "Jobi", + "Jobie", + "Jobina", + "Joby", + "Jobye", + "Jobyna", + "Jocelin", + "Joceline", + "Jocelyn", + "Jocelyne", + "Jodee", + "Jodi", + "Jodie", + "Jody", + "Joeann", + "Joela", + "Joelie", + "Joell", + "Joella", + "Joelle", + "Joellen", + "Joelly", + "Joellyn", + "Joelynn", + "Joete", + "Joey", + "Johanna", + "Johannah", + "Johna", + "Johnath", + "Johnette", + "Johnna", + "Joice", + "Jojo", + "Jolee", + "Joleen", + "Jolene", + "Joletta", + "Joli", + "Jolie", + "Joline", + "Joly", + "Jolyn", + "Jolynn", + "Jonell", + "Joni", + "Jonie", + "Jonis", + "Jordain", + "Jordan", + "Jordana", + "Jordanna", + "Jorey", + "Jori", + "Jorie", + "Jorrie", + "Jorry", + "Joscelin", + "Josee", + "Josefa", + "Josefina", + "Josepha", + "Josephina", + "Josephine", + "Josey", + "Josi", + "Josie", + "Josselyn", + "Josy", + "Jourdan", + "Joy", + "Joya", + "Joyan", + "Joyann", + "Joyce", + "Joycelin", + "Joye", + "Jsandye", + "Juana", + "Juanita", + "Judi", + "Judie", + "Judith", + "Juditha", + "Judy", + "Judye", + "Juieta", + "Julee", + "Juli", + "Julia", + "Juliana", + "Juliane", + "Juliann", + "Julianna", + "Julianne", + "Julie", + "Julienne", + "Juliet", + "Julieta", + "Julietta", + "Juliette", + "Julina", + "Juline", + "Julissa", + "Julita", + "June", + "Junette", + "Junia", + "Junie", + "Junina", + "Justina", + "Justine", + "Justinn", + "Jyoti", + "Kacey", + "Kacie", + "Kacy", + "Kaela", + "Kai", + "Kaia", + "Kaila", + "Kaile", + "Kailey", + "Kaitlin", + "Kaitlyn", + "Kaitlynn", + "Kaja", + "Kakalina", + "Kala", + "Kaleena", + "Kali", + "Kalie", + "Kalila", + "Kalina", + "Kalinda", + "Kalindi", + "Kalli", + "Kally", + "Kameko", + "Kamila", + "Kamilah", + "Kamillah", + "Kandace", + "Kandy", + "Kania", + "Kanya", + "Kara", + "Kara-Lynn", + "Karalee", + "Karalynn", + "Kare", + "Karee", + "Karel", + "Karen", + "Karena", + "Kari", + "Karia", + "Karie", + "Karil", + "Karilynn", + "Karin", + "Karina", + "Karine", + "Kariotta", + "Karisa", + "Karissa", + "Karita", + "Karla", + "Karlee", + "Karleen", + "Karlen", + "Karlene", + "Karlie", + "Karlotta", + "Karlotte", + "Karly", + "Karlyn", + "Karmen", + "Karna", + "Karol", + "Karola", + "Karole", + "Karolina", + "Karoline", + "Karoly", + "Karon", + "Karrah", + "Karrie", + "Karry", + "Kary", + "Karyl", + "Karylin", + "Karyn", + "Kasey", + "Kass", + "Kassandra", + "Kassey", + "Kassi", + "Kassia", + "Kassie", + "Kat", + "Kata", + "Katalin", + "Kate", + "Katee", + "Katerina", + "Katerine", + "Katey", + "Kath", + "Katha", + "Katharina", + "Katharine", + "Katharyn", + "Kathe", + "Katherina", + "Katherine", + "Katheryn", + "Kathi", + "Kathie", + "Kathleen", + "Kathlin", + "Kathrine", + "Kathryn", + "Kathryne", + "Kathy", + "Kathye", + "Kati", + "Katie", + "Katina", + "Katine", + "Katinka", + "Katleen", + "Katlin", + "Katrina", + "Katrine", + "Katrinka", + "Katti", + "Kattie", + "Katuscha", + "Katusha", + "Katy", + "Katya", + "Kay", + "Kaycee", + "Kaye", + "Kayla", + "Kayle", + "Kaylee", + "Kayley", + "Kaylil", + "Kaylyn", + "Keeley", + "Keelia", + "Keely", + "Kelcey", + "Kelci", + "Kelcie", + "Kelcy", + "Kelila", + "Kellen", + "Kelley", + "Kelli", + "Kellia", + "Kellie", + "Kellina", + "Kellsie", + "Kelly", + "Kellyann", + "Kelsey", + "Kelsi", + "Kelsy", + "Kendra", + "Kendre", + "Kenna", + "Keri", + "Keriann", + "Kerianne", + "Kerri", + "Kerrie", + "Kerrill", + "Kerrin", + "Kerry", + "Kerstin", + "Kesley", + "Keslie", + "Kessia", + "Kessiah", + "Ketti", + "Kettie", + "Ketty", + "Kevina", + "Kevyn", + "Ki", + "Kiah", + "Kial", + "Kiele", + "Kiersten", + "Kikelia", + "Kiley", + "Kim", + "Kimberlee", + "Kimberley", + "Kimberli", + "Kimberly", + "Kimberlyn", + "Kimbra", + "Kimmi", + "Kimmie", + "Kimmy", + "Kinna", + "Kip", + "Kipp", + "Kippie", + "Kippy", + "Kira", + "Kirbee", + "Kirbie", + "Kirby", + "Kiri", + "Kirsten", + "Kirsteni", + "Kirsti", + "Kirstin", + "Kirstyn", + "Kissee", + "Kissiah", + "Kissie", + "Kit", + "Kitti", + "Kittie", + "Kitty", + "Kizzee", + "Kizzie", + "Klara", + "Klarika", + "Klarrisa", + "Konstance", + "Konstanze", + "Koo", + "Kora", + "Koral", + "Koralle", + "Kordula", + "Kore", + "Korella", + "Koren", + "Koressa", + "Kori", + "Korie", + "Korney", + "Korrie", + "Korry", + "Kris", + "Krissie", + "Krissy", + "Krista", + "Kristal", + "Kristan", + "Kriste", + "Kristel", + "Kristen", + "Kristi", + "Kristien", + "Kristin", + "Kristina", + "Kristine", + "Kristy", + "Kristyn", + "Krysta", + "Krystal", + "Krystalle", + "Krystle", + "Krystyna", + "Kyla", + "Kyle", + "Kylen", + "Kylie", + "Kylila", + "Kylynn", + "Kym", + "Kynthia", + "Kyrstin", + "La Verne", + "Lacee", + "Lacey", + "Lacie", + "Lacy", + "Ladonna", + "Laetitia", + "Laina", + "Lainey", + "Lana", + "Lanae", + "Lane", + "Lanette", + "Laney", + "Lani", + "Lanie", + "Lanita", + "Lanna", + "Lanni", + "Lanny", + "Lara", + "Laraine", + "Lari", + "Larina", + "Larine", + "Larisa", + "Larissa", + "Lark", + "Laryssa", + "Latashia", + "Latia", + "Latisha", + "Latrena", + "Latrina", + "Laura", + "Lauraine", + "Laural", + "Lauralee", + "Laure", + "Lauree", + "Laureen", + "Laurel", + "Laurella", + "Lauren", + "Laurena", + "Laurene", + "Lauretta", + "Laurette", + "Lauri", + "Laurianne", + "Laurice", + "Laurie", + "Lauryn", + "Lavena", + "Laverna", + "Laverne", + "Lavina", + "Lavinia", + "Lavinie", + "Layla", + "Layne", + "Layney", + "Lea", + "Leah", + "Leandra", + "Leann", + "Leanna", + "Leanor", + "Leanora", + "Lebbie", + "Leda", + "Lee", + "Leeann", + "Leeanne", + "Leela", + "Leelah", + "Leena", + "Leesa", + "Leese", + "Legra", + "Leia", + "Leigh", + "Leigha", + "Leila", + "Leilah", + "Leisha", + "Lela", + "Lelah", + "Leland", + "Lelia", + "Lena", + "Lenee", + "Lenette", + "Lenka", + "Lenna", + "Lenora", + "Lenore", + "Leodora", + "Leoine", + "Leola", + "Leoline", + "Leona", + "Leonanie", + "Leone", + "Leonelle", + "Leonie", + "Leonora", + "Leonore", + "Leontine", + "Leontyne", + "Leora", + "Leshia", + "Lesley", + "Lesli", + "Leslie", + "Lesly", + "Lesya", + "Leta", + "Lethia", + "Leticia", + "Letisha", + "Letitia", + "Letizia", + "Letta", + "Letti", + "Lettie", + "Letty", + "Lexi", + "Lexie", + "Lexine", + "Lexis", + "Lexy", + "Leyla", + "Lezlie", + "Lia", + "Lian", + "Liana", + "Liane", + "Lianna", + "Lianne", + "Lib", + "Libbey", + "Libbi", + "Libbie", + "Libby", + "Licha", + "Lida", + "Lidia", + "Liesa", + "Lil", + "Lila", + "Lilah", + "Lilas", + "Lilia", + "Lilian", + "Liliane", + "Lilias", + "Lilith", + "Lilla", + "Lilli", + "Lillian", + "Lillis", + "Lilllie", + "Lilly", + "Lily", + "Lilyan", + "Lin", + "Lina", + "Lind", + "Linda", + "Lindi", + "Lindie", + "Lindsay", + "Lindsey", + "Lindsy", + "Lindy", + "Linea", + "Linell", + "Linet", + "Linette", + "Linn", + "Linnea", + "Linnell", + "Linnet", + "Linnie", + "Linzy", + "Lira", + "Lisa", + "Lisabeth", + "Lisbeth", + "Lise", + "Lisetta", + "Lisette", + "Lisha", + "Lishe", + "Lissa", + "Lissi", + "Lissie", + "Lissy", + "Lita", + "Liuka", + "Liv", + "Liva", + "Livia", + "Livvie", + "Livvy", + "Livvyy", + "Livy", + "Liz", + "Liza", + "Lizabeth", + "Lizbeth", + "Lizette", + "Lizzie", + "Lizzy", + "Loella", + "Lois", + "Loise", + "Lola", + "Loleta", + "Lolita", + "Lolly", + "Lona", + "Lonee", + "Loni", + "Lonna", + "Lonni", + "Lonnie", + "Lora", + "Lorain", + "Loraine", + "Loralee", + "Loralie", + "Loralyn", + "Loree", + "Loreen", + "Lorelei", + "Lorelle", + "Loren", + "Lorena", + "Lorene", + "Lorenza", + "Loretta", + "Lorette", + "Lori", + "Loria", + "Lorianna", + "Lorianne", + "Lorie", + "Lorilee", + "Lorilyn", + "Lorinda", + "Lorine", + "Lorita", + "Lorna", + "Lorne", + "Lorraine", + "Lorrayne", + "Lorri", + "Lorrie", + "Lorrin", + "Lorry", + "Lory", + "Lotta", + "Lotte", + "Lotti", + "Lottie", + "Lotty", + "Lou", + "Louella", + "Louisa", + "Louise", + "Louisette", + "Loutitia", + "Lu", + "Luce", + "Luci", + "Lucia", + "Luciana", + "Lucie", + "Lucienne", + "Lucila", + "Lucilia", + "Lucille", + "Lucina", + "Lucinda", + "Lucine", + "Lucita", + "Lucky", + "Lucretia", + "Lucy", + "Ludovika", + "Luella", + "Luelle", + "Luisa", + "Luise", + "Lula", + "Lulita", + "Lulu", + "Lura", + "Lurette", + "Lurleen", + "Lurlene", + "Lurline", + "Lusa", + "Luz", + "Lyda", + "Lydia", + "Lydie", + "Lyn", + "Lynda", + "Lynde", + "Lyndel", + "Lyndell", + "Lyndsay", + "Lyndsey", + "Lyndsie", + "Lyndy", + "Lynea", + "Lynelle", + "Lynett", + "Lynette", + "Lynn", + "Lynna", + "Lynne", + "Lynnea", + "Lynnell", + "Lynnelle", + "Lynnet", + "Lynnett", + "Lynnette", + "Lynsey", + "Lyssa", + "Mab", + "Mabel", + "Mabelle", + "Mable", + "Mada", + "Madalena", + "Madalyn", + "Maddalena", + "Maddi", + "Maddie", + "Maddy", + "Madel", + "Madelaine", + "Madeleine", + "Madelena", + "Madelene", + "Madelin", + "Madelina", + "Madeline", + "Madella", + "Madelle", + "Madelon", + "Madelyn", + "Madge", + "Madlen", + "Madlin", + "Madonna", + "Mady", + "Mae", + "Maegan", + "Mag", + "Magda", + "Magdaia", + "Magdalen", + "Magdalena", + "Magdalene", + "Maggee", + "Maggi", + "Maggie", + "Maggy", + "Mahala", + "Mahalia", + "Maia", + "Maible", + "Maiga", + "Maighdiln", + "Mair", + "Maire", + "Maisey", + "Maisie", + "Maitilde", + "Mala", + "Malanie", + "Malena", + "Malia", + "Malina", + "Malinda", + "Malinde", + "Malissa", + "Malissia", + "Mallissa", + "Mallorie", + "Mallory", + "Malorie", + "Malory", + "Malva", + "Malvina", + "Malynda", + "Mame", + "Mamie", + "Manda", + "Mandi", + "Mandie", + "Mandy", + "Manon", + "Manya", + "Mara", + "Marabel", + "Marcela", + "Marcelia", + "Marcella", + "Marcelle", + "Marcellina", + "Marcelline", + "Marchelle", + "Marci", + "Marcia", + "Marcie", + "Marcile", + "Marcille", + "Marcy", + "Mareah", + "Maren", + "Marena", + "Maressa", + "Marga", + "Margalit", + "Margalo", + "Margaret", + "Margareta", + "Margarete", + "Margaretha", + "Margarethe", + "Margaretta", + "Margarette", + "Margarita", + "Margaux", + "Marge", + "Margeaux", + "Margery", + "Marget", + "Margette", + "Margi", + "Margie", + "Margit", + "Margo", + "Margot", + "Margret", + "Marguerite", + "Margy", + "Mari", + "Maria", + "Mariam", + "Marian", + "Mariana", + "Mariann", + "Marianna", + "Marianne", + "Maribel", + "Maribelle", + "Maribeth", + "Marice", + "Maridel", + "Marie", + "Marie-Ann", + "Marie-Jeanne", + "Marieann", + "Mariejeanne", + "Mariel", + "Mariele", + "Marielle", + "Mariellen", + "Marietta", + "Mariette", + "Marigold", + "Marijo", + "Marika", + "Marilee", + "Marilin", + "Marillin", + "Marilyn", + "Marin", + "Marina", + "Marinna", + "Marion", + "Mariquilla", + "Maris", + "Marisa", + "Mariska", + "Marissa", + "Marita", + "Maritsa", + "Mariya", + "Marj", + "Marja", + "Marje", + "Marji", + "Marjie", + "Marjorie", + "Marjory", + "Marjy", + "Marketa", + "Marla", + "Marlane", + "Marleah", + "Marlee", + "Marleen", + "Marlena", + "Marlene", + "Marley", + "Marlie", + "Marline", + "Marlo", + "Marlyn", + "Marna", + "Marne", + "Marney", + "Marni", + "Marnia", + "Marnie", + "Marquita", + "Marrilee", + "Marris", + "Marrissa", + "Marsha", + "Marsiella", + "Marta", + "Martelle", + "Martguerita", + "Martha", + "Marthe", + "Marthena", + "Marti", + "Martica", + "Martie", + "Martina", + "Martita", + "Marty", + "Martynne", + "Mary", + "Marya", + "Maryann", + "Maryanna", + "Maryanne", + "Marybelle", + "Marybeth", + "Maryellen", + "Maryjane", + "Maryjo", + "Maryl", + "Marylee", + "Marylin", + "Marylinda", + "Marylou", + "Marylynne", + "Maryrose", + "Marys", + "Marysa", + "Masha", + "Matelda", + "Mathilda", + "Mathilde", + "Matilda", + "Matilde", + "Matti", + "Mattie", + "Matty", + "Maud", + "Maude", + "Maudie", + "Maura", + "Maure", + "Maureen", + "Maureene", + "Maurene", + "Maurine", + "Maurise", + "Maurita", + "Maurizia", + "Mavis", + "Mavra", + "Max", + "Maxi", + "Maxie", + "Maxine", + "Maxy", + "May", + "Maybelle", + "Maye", + "Mead", + "Meade", + "Meagan", + "Meaghan", + "Meara", + "Mechelle", + "Meg", + "Megan", + "Megen", + "Meggi", + "Meggie", + "Meggy", + "Meghan", + "Meghann", + "Mehetabel", + "Mei", + "Mel", + "Mela", + "Melamie", + "Melania", + "Melanie", + "Melantha", + "Melany", + "Melba", + "Melesa", + "Melessa", + "Melicent", + "Melina", + "Melinda", + "Melinde", + "Melisa", + "Melisande", + "Melisandra", + "Melisenda", + "Melisent", + "Melissa", + "Melisse", + "Melita", + "Melitta", + "Mella", + "Melli", + "Mellicent", + "Mellie", + "Mellisa", + "Mellisent", + "Melloney", + "Melly", + "Melodee", + "Melodie", + "Melody", + "Melonie", + "Melony", + "Melosa", + "Melva", + "Mercedes", + "Merci", + "Mercie", + "Mercy", + "Meredith", + "Meredithe", + "Meridel", + "Meridith", + "Meriel", + "Merilee", + "Merilyn", + "Meris", + "Merissa", + "Merl", + "Merla", + "Merle", + "Merlina", + "Merline", + "Merna", + "Merola", + "Merralee", + "Merridie", + "Merrie", + "Merrielle", + "Merrile", + "Merrilee", + "Merrili", + "Merrill", + "Merrily", + "Merry", + "Mersey", + "Meryl", + "Meta", + "Mia", + "Micaela", + "Michaela", + "Michaelina", + "Michaeline", + "Michaella", + "Michal", + "Michel", + "Michele", + "Michelina", + "Micheline", + "Michell", + "Michelle", + "Micki", + "Mickie", + "Micky", + "Midge", + "Mignon", + "Mignonne", + "Miguela", + "Miguelita", + "Mikaela", + "Mil", + "Mildred", + "Mildrid", + "Milena", + "Milicent", + "Milissent", + "Milka", + "Milli", + "Millicent", + "Millie", + "Millisent", + "Milly", + "Milzie", + "Mimi", + "Min", + "Mina", + "Minda", + "Mindy", + "Minerva", + "Minetta", + "Minette", + "Minna", + "Minnaminnie", + "Minne", + "Minni", + "Minnie", + "Minnnie", + "Minny", + "Minta", + "Miof Mela", + "Miquela", + "Mira", + "Mirabel", + "Mirabella", + "Mirabelle", + "Miran", + "Miranda", + "Mireielle", + "Mireille", + "Mirella", + "Mirelle", + "Miriam", + "Mirilla", + "Mirna", + "Misha", + "Missie", + "Missy", + "Misti", + "Misty", + "Mitzi", + "Modesta", + "Modestia", + "Modestine", + "Modesty", + "Moina", + "Moira", + "Moll", + "Mollee", + "Molli", + "Mollie", + "Molly", + "Mommy", + "Mona", + "Monah", + "Monica", + "Monika", + "Monique", + "Mora", + "Moreen", + "Morena", + "Morgan", + "Morgana", + "Morganica", + "Morganne", + "Morgen", + "Moria", + "Morissa", + "Morna", + "Moselle", + "Moyna", + "Moyra", + "Mozelle", + "Muffin", + "Mufi", + "Mufinella", + "Muire", + "Mureil", + "Murial", + "Muriel", + "Murielle", + "Myra", + "Myrah", + "Myranda", + "Myriam", + "Myrilla", + "Myrle", + "Myrlene", + "Myrna", + "Myrta", + "Myrtia", + "Myrtice", + "Myrtie", + "Myrtle", + "Nada", + "Nadean", + "Nadeen", + "Nadia", + "Nadine", + "Nadiya", + "Nady", + "Nadya", + "Nalani", + "Nan", + "Nana", + "Nananne", + "Nance", + "Nancee", + "Nancey", + "Nanci", + "Nancie", + "Nancy", + "Nanete", + "Nanette", + "Nani", + "Nanice", + "Nanine", + "Nannette", + "Nanni", + "Nannie", + "Nanny", + "Nanon", + "Naoma", + "Naomi", + "Nara", + "Nari", + "Nariko", + "Nat", + "Nata", + "Natala", + "Natalee", + "Natalie", + "Natalina", + "Nataline", + "Natalya", + "Natasha", + "Natassia", + "Nathalia", + "Nathalie", + "Natividad", + "Natka", + "Natty", + "Neala", + "Neda", + "Nedda", + "Nedi", + "Neely", + "Neila", + "Neile", + "Neilla", + "Neille", + "Nelia", + "Nelie", + "Nell", + "Nelle", + "Nelli", + "Nellie", + "Nelly", + "Nerissa", + "Nerita", + "Nert", + "Nerta", + "Nerte", + "Nerti", + "Nertie", + "Nerty", + "Nessa", + "Nessi", + "Nessie", + "Nessy", + "Nesta", + "Netta", + "Netti", + "Nettie", + "Nettle", + "Netty", + "Nevsa", + "Neysa", + "Nichol", + "Nichole", + "Nicholle", + "Nicki", + "Nickie", + "Nicky", + "Nicol", + "Nicola", + "Nicole", + "Nicolea", + "Nicolette", + "Nicoli", + "Nicolina", + "Nicoline", + "Nicolle", + "Nikaniki", + "Nike", + "Niki", + "Nikki", + "Nikkie", + "Nikoletta", + "Nikolia", + "Nina", + "Ninetta", + "Ninette", + "Ninnetta", + "Ninnette", + "Ninon", + "Nissa", + "Nisse", + "Nissie", + "Nissy", + "Nita", + "Nixie", + "Noami", + "Noel", + "Noelani", + "Noell", + "Noella", + "Noelle", + "Noellyn", + "Noelyn", + "Noemi", + "Nola", + "Nolana", + "Nolie", + "Nollie", + "Nomi", + "Nona", + "Nonah", + "Noni", + "Nonie", + "Nonna", + "Nonnah", + "Nora", + "Norah", + "Norean", + "Noreen", + "Norene", + "Norina", + "Norine", + "Norma", + "Norri", + "Norrie", + "Norry", + "Novelia", + "Nydia", + "Nyssa", + "Octavia", + "Odele", + "Odelia", + "Odelinda", + "Odella", + "Odelle", + "Odessa", + "Odetta", + "Odette", + "Odilia", + "Odille", + "Ofelia", + "Ofella", + "Ofilia", + "Ola", + "Olenka", + "Olga", + "Olia", + "Olimpia", + "Olive", + "Olivette", + "Olivia", + "Olivie", + "Oliy", + "Ollie", + "Olly", + "Olva", + "Olwen", + "Olympe", + "Olympia", + "Olympie", + "Ondrea", + "Oneida", + "Onida", + "Oona", + "Opal", + "Opalina", + "Opaline", + "Ophelia", + "Ophelie", + "Ora", + "Oralee", + "Oralia", + "Oralie", + "Oralla", + "Oralle", + "Orel", + "Orelee", + "Orelia", + "Orelie", + "Orella", + "Orelle", + "Oriana", + "Orly", + "Orsa", + "Orsola", + "Ortensia", + "Otha", + "Othelia", + "Othella", + "Othilia", + "Othilie", + "Ottilie", + "Page", + "Paige", + "Paloma", + "Pam", + "Pamela", + "Pamelina", + "Pamella", + "Pammi", + "Pammie", + "Pammy", + "Pandora", + "Pansie", + "Pansy", + "Paola", + "Paolina", + "Papagena", + "Pat", + "Patience", + "Patrica", + "Patrice", + "Patricia", + "Patrizia", + "Patsy", + "Patti", + "Pattie", + "Patty", + "Paula", + "Paule", + "Pauletta", + "Paulette", + "Pauli", + "Paulie", + "Paulina", + "Pauline", + "Paulita", + "Pauly", + "Pavia", + "Pavla", + "Pearl", + "Pearla", + "Pearle", + "Pearline", + "Peg", + "Pegeen", + "Peggi", + "Peggie", + "Peggy", + "Pen", + "Penelopa", + "Penelope", + "Penni", + "Pennie", + "Penny", + "Pepi", + "Pepita", + "Peri", + "Peria", + "Perl", + "Perla", + "Perle", + "Perri", + "Perrine", + "Perry", + "Persis", + "Pet", + "Peta", + "Petra", + "Petrina", + "Petronella", + "Petronia", + "Petronilla", + "Petronille", + "Petunia", + "Phaedra", + "Phaidra", + "Phebe", + "Phedra", + "Phelia", + "Phil", + "Philipa", + "Philippa", + "Philippe", + "Philippine", + "Philis", + "Phillida", + "Phillie", + "Phillis", + "Philly", + "Philomena", + "Phoebe", + "Phylis", + "Phyllida", + "Phyllis", + "Phyllys", + "Phylys", + "Pia", + "Pier", + "Pierette", + "Pierrette", + "Pietra", + "Piper", + "Pippa", + "Pippy", + "Polly", + "Pollyanna", + "Pooh", + "Poppy", + "Portia", + "Pris", + "Prisca", + "Priscella", + "Priscilla", + "Prissie", + "Pru", + "Prudence", + "Prudi", + "Prudy", + "Prue", + "Queenie", + "Quentin", + "Querida", + "Quinn", + "Quinta", + "Quintana", + "Quintilla", + "Quintina", + "Rachael", + "Rachel", + "Rachele", + "Rachelle", + "Rae", + "Raeann", + "Raf", + "Rafa", + "Rafaela", + "Rafaelia", + "Rafaelita", + "Rahal", + "Rahel", + "Raina", + "Raine", + "Rakel", + "Ralina", + "Ramona", + "Ramonda", + "Rana", + "Randa", + "Randee", + "Randene", + "Randi", + "Randie", + "Randy", + "Ranee", + "Rani", + "Rania", + "Ranice", + "Ranique", + "Ranna", + "Raphaela", + "Raquel", + "Raquela", + "Rasia", + "Rasla", + "Raven", + "Ray", + "Raychel", + "Raye", + "Rayna", + "Raynell", + "Rayshell", + "Rea", + "Reba", + "Rebbecca", + "Rebe", + "Rebeca", + "Rebecca", + "Rebecka", + "Rebeka", + "Rebekah", + "Rebekkah", + "Ree", + "Reeba", + "Reena", + "Reeta", + "Reeva", + "Regan", + "Reggi", + "Reggie", + "Regina", + "Regine", + "Reiko", + "Reina", + "Reine", + "Remy", + "Rena", + "Renae", + "Renata", + "Renate", + "Rene", + "Renee", + "Renell", + "Renelle", + "Renie", + "Rennie", + "Reta", + "Retha", + "Revkah", + "Rey", + "Reyna", + "Rhea", + "Rheba", + "Rheta", + "Rhetta", + "Rhiamon", + "Rhianna", + "Rhianon", + "Rhoda", + "Rhodia", + "Rhodie", + "Rhody", + "Rhona", + "Rhonda", + "Riane", + "Riannon", + "Rianon", + "Rica", + "Ricca", + "Rici", + "Ricki", + "Rickie", + "Ricky", + "Riki", + "Rikki", + "Rina", + "Risa", + "Rita", + "Riva", + "Rivalee", + "Rivi", + "Rivkah", + "Rivy", + "Roana", + "Roanna", + "Roanne", + "Robbi", + "Robbie", + "Robbin", + "Robby", + "Robbyn", + "Robena", + "Robenia", + "Roberta", + "Robin", + "Robina", + "Robinet", + "Robinett", + "Robinetta", + "Robinette", + "Robinia", + "Roby", + "Robyn", + "Roch", + "Rochell", + "Rochella", + "Rochelle", + "Rochette", + "Roda", + "Rodi", + "Rodie", + "Rodina", + "Rois", + "Romola", + "Romona", + "Romonda", + "Romy", + "Rona", + "Ronalda", + "Ronda", + "Ronica", + "Ronna", + "Ronni", + "Ronnica", + "Ronnie", + "Ronny", + "Roobbie", + "Rora", + "Rori", + "Rorie", + "Rory", + "Ros", + "Rosa", + "Rosabel", + "Rosabella", + "Rosabelle", + "Rosaleen", + "Rosalia", + "Rosalie", + "Rosalind", + "Rosalinda", + "Rosalinde", + "Rosaline", + "Rosalyn", + "Rosalynd", + "Rosamond", + "Rosamund", + "Rosana", + "Rosanna", + "Rosanne", + "Rose", + "Roseann", + "Roseanna", + "Roseanne", + "Roselia", + "Roselin", + "Roseline", + "Rosella", + "Roselle", + "Rosemaria", + "Rosemarie", + "Rosemary", + "Rosemonde", + "Rosene", + "Rosetta", + "Rosette", + "Roshelle", + "Rosie", + "Rosina", + "Rosita", + "Roslyn", + "Rosmunda", + "Rosy", + "Row", + "Rowe", + "Rowena", + "Roxana", + "Roxane", + "Roxanna", + "Roxanne", + "Roxi", + "Roxie", + "Roxine", + "Roxy", + "Roz", + "Rozalie", + "Rozalin", + "Rozamond", + "Rozanna", + "Rozanne", + "Roze", + "Rozele", + "Rozella", + "Rozelle", + "Rozina", + "Rubetta", + "Rubi", + "Rubia", + "Rubie", + "Rubina", + "Ruby", + "Ruperta", + "Ruth", + "Ruthann", + "Ruthanne", + "Ruthe", + "Ruthi", + "Ruthie", + "Ruthy", + "Ryann", + "Rycca", + "Saba", + "Sabina", + "Sabine", + "Sabra", + "Sabrina", + "Sacha", + "Sada", + "Sadella", + "Sadie", + "Sadye", + "Saidee", + "Sal", + "Salaidh", + "Sallee", + "Salli", + "Sallie", + "Sally", + "Sallyann", + "Sallyanne", + "Saloma", + "Salome", + "Salomi", + "Sam", + "Samantha", + "Samara", + "Samaria", + "Sammy", + "Sande", + "Sandi", + "Sandie", + "Sandra", + "Sandy", + "Sandye", + "Sapphira", + "Sapphire", + "Sara", + "Sara-Ann", + "Saraann", + "Sarah", + "Sarajane", + "Saree", + "Sarena", + "Sarene", + "Sarette", + "Sari", + "Sarina", + "Sarine", + "Sarita", + "Sascha", + "Sasha", + "Sashenka", + "Saudra", + "Saundra", + "Savina", + "Sayre", + "Scarlet", + "Scarlett", + "Sean", + "Seana", + "Seka", + "Sela", + "Selena", + "Selene", + "Selestina", + "Selia", + "Selie", + "Selina", + "Selinda", + "Seline", + "Sella", + "Selle", + "Selma", + "Sena", + "Sephira", + "Serena", + "Serene", + "Shae", + "Shaina", + "Shaine", + "Shalna", + "Shalne", + "Shana", + "Shanda", + "Shandee", + "Shandeigh", + "Shandie", + "Shandra", + "Shandy", + "Shane", + "Shani", + "Shanie", + "Shanna", + "Shannah", + "Shannen", + "Shannon", + "Shanon", + "Shanta", + "Shantee", + "Shara", + "Sharai", + "Shari", + "Sharia", + "Sharity", + "Sharl", + "Sharla", + "Sharleen", + "Sharlene", + "Sharline", + "Sharon", + "Sharona", + "Sharron", + "Sharyl", + "Shaun", + "Shauna", + "Shawn", + "Shawna", + "Shawnee", + "Shay", + "Shayla", + "Shaylah", + "Shaylyn", + "Shaylynn", + "Shayna", + "Shayne", + "Shea", + "Sheba", + "Sheela", + "Sheelagh", + "Sheelah", + "Sheena", + "Sheeree", + "Sheila", + "Sheila-Kathryn", + "Sheilah", + "Shel", + "Shela", + "Shelagh", + "Shelba", + "Shelbi", + "Shelby", + "Shelia", + "Shell", + "Shelley", + "Shelli", + "Shellie", + "Shelly", + "Shena", + "Sher", + "Sheree", + "Sheri", + "Sherie", + "Sherill", + "Sherilyn", + "Sherline", + "Sherri", + "Sherrie", + "Sherry", + "Sherye", + "Sheryl", + "Shina", + "Shir", + "Shirl", + "Shirlee", + "Shirleen", + "Shirlene", + "Shirley", + "Shirline", + "Shoshana", + "Shoshanna", + "Siana", + "Sianna", + "Sib", + "Sibbie", + "Sibby", + "Sibeal", + "Sibel", + "Sibella", + "Sibelle", + "Sibilla", + "Sibley", + "Sibyl", + "Sibylla", + "Sibylle", + "Sidoney", + "Sidonia", + "Sidonnie", + "Sigrid", + "Sile", + "Sileas", + "Silva", + "Silvana", + "Silvia", + "Silvie", + "Simona", + "Simone", + "Simonette", + "Simonne", + "Sindee", + "Siobhan", + "Sioux", + "Siouxie", + "Sisely", + "Sisile", + "Sissie", + "Sissy", + "Siusan", + "Sofia", + "Sofie", + "Sondra", + "Sonia", + "Sonja", + "Sonni", + "Sonnie", + "Sonnnie", + "Sonny", + "Sonya", + "Sophey", + "Sophi", + "Sophia", + "Sophie", + "Sophronia", + "Sorcha", + "Sosanna", + "Stace", + "Stacee", + "Stacey", + "Staci", + "Stacia", + "Stacie", + "Stacy", + "Stafani", + "Star", + "Starla", + "Starlene", + "Starlin", + "Starr", + "Stefa", + "Stefania", + "Stefanie", + "Steffane", + "Steffi", + "Steffie", + "Stella", + "Stepha", + "Stephana", + "Stephani", + "Stephanie", + "Stephannie", + "Stephenie", + "Stephi", + "Stephie", + "Stephine", + "Stesha", + "Stevana", + "Stevena", + "Stoddard", + "Storm", + "Stormi", + "Stormie", + "Stormy", + "Sue", + "Suellen", + "Sukey", + "Suki", + "Sula", + "Sunny", + "Sunshine", + "Susan", + "Susana", + "Susanetta", + "Susann", + "Susanna", + "Susannah", + "Susanne", + "Susette", + "Susi", + "Susie", + "Susy", + "Suzann", + "Suzanna", + "Suzanne", + "Suzette", + "Suzi", + "Suzie", + "Suzy", + "Sybil", + "Sybila", + "Sybilla", + "Sybille", + "Sybyl", + "Sydel", + "Sydelle", + "Sydney", + "Sylvia", + "Tabatha", + "Tabbatha", + "Tabbi", + "Tabbie", + "Tabbitha", + "Tabby", + "Tabina", + "Tabitha", + "Taffy", + "Talia", + "Tallia", + "Tallie", + "Tallou", + "Tallulah", + "Tally", + "Talya", + "Talyah", + "Tamar", + "Tamara", + "Tamarah", + "Tamarra", + "Tamera", + "Tami", + "Tamiko", + "Tamma", + "Tammara", + "Tammi", + "Tammie", + "Tammy", + "Tamqrah", + "Tamra", + "Tana", + "Tandi", + "Tandie", + "Tandy", + "Tanhya", + "Tani", + "Tania", + "Tanitansy", + "Tansy", + "Tanya", + "Tara", + "Tarah", + "Tarra", + "Tarrah", + "Taryn", + "Tasha", + "Tasia", + "Tate", + "Tatiana", + "Tatiania", + "Tatum", + "Tawnya", + "Tawsha", + "Ted", + "Tedda", + "Teddi", + "Teddie", + "Teddy", + "Tedi", + "Tedra", + "Teena", + "TEirtza", + "Teodora", + "Tera", + "Teresa", + "Terese", + "Teresina", + "Teresita", + "Teressa", + "Teri", + "Teriann", + "Terra", + "Terri", + "Terrie", + "Terrijo", + "Terry", + "Terrye", + "Tersina", + "Terza", + "Tess", + "Tessa", + "Tessi", + "Tessie", + "Tessy", + "Thalia", + "Thea", + "Theadora", + "Theda", + "Thekla", + "Thelma", + "Theo", + "Theodora", + "Theodosia", + "Theresa", + "Therese", + "Theresina", + "Theresita", + "Theressa", + "Therine", + "Thia", + "Thomasa", + "Thomasin", + "Thomasina", + "Thomasine", + "Tiena", + "Tierney", + "Tiertza", + "Tiff", + "Tiffani", + "Tiffanie", + "Tiffany", + "Tiffi", + "Tiffie", + "Tiffy", + "Tilda", + "Tildi", + "Tildie", + "Tildy", + "Tillie", + "Tilly", + "Tim", + "Timi", + "Timmi", + "Timmie", + "Timmy", + "Timothea", + "Tina", + "Tine", + "Tiphani", + "Tiphanie", + "Tiphany", + "Tish", + "Tisha", + "Tobe", + "Tobey", + "Tobi", + "Toby", + "Tobye", + "Toinette", + "Toma", + "Tomasina", + "Tomasine", + "Tomi", + "Tommi", + "Tommie", + "Tommy", + "Toni", + "Tonia", + "Tonie", + "Tony", + "Tonya", + "Tonye", + "Tootsie", + "Torey", + "Tori", + "Torie", + "Torrie", + "Tory", + "Tova", + "Tove", + "Tracee", + "Tracey", + "Traci", + "Tracie", + "Tracy", + "Trenna", + "Tresa", + "Trescha", + "Tressa", + "Tricia", + "Trina", + "Trish", + "Trisha", + "Trista", + "Trix", + "Trixi", + "Trixie", + "Trixy", + "Truda", + "Trude", + "Trudey", + "Trudi", + "Trudie", + "Trudy", + "Trula", + "Tuesday", + "Twila", + "Twyla", + "Tybi", + "Tybie", + "Tyne", + "Ula", + "Ulla", + "Ulrica", + "Ulrika", + "Ulrikaumeko", + "Ulrike", + "Umeko", + "Una", + "Ursa", + "Ursala", + "Ursola", + "Ursula", + "Ursulina", + "Ursuline", + "Uta", + "Val", + "Valaree", + "Valaria", + "Vale", + "Valeda", + "Valencia", + "Valene", + "Valenka", + "Valentia", + "Valentina", + "Valentine", + "Valera", + "Valeria", + "Valerie", + "Valery", + "Valerye", + "Valida", + "Valina", + "Valli", + "Vallie", + "Vally", + "Valma", + "Valry", + "Van", + "Vanda", + "Vanessa", + "Vania", + "Vanna", + "Vanni", + "Vannie", + "Vanny", + "Vanya", + "Veda", + "Velma", + "Velvet", + "Venita", + "Venus", + "Vera", + "Veradis", + "Vere", + "Verena", + "Verene", + "Veriee", + "Verile", + "Verina", + "Verine", + "Verla", + "Verna", + "Vernice", + "Veronica", + "Veronika", + "Veronike", + "Veronique", + "Vevay", + "Vi", + "Vicki", + "Vickie", + "Vicky", + "Victoria", + "Vida", + "Viki", + "Vikki", + "Vikky", + "Vilhelmina", + "Vilma", + "Vin", + "Vina", + "Vinita", + "Vinni", + "Vinnie", + "Vinny", + "Viola", + "Violante", + "Viole", + "Violet", + "Violetta", + "Violette", + "Virgie", + "Virgina", + "Virginia", + "Virginie", + "Vita", + "Vitia", + "Vitoria", + "Vittoria", + "Viv", + "Viva", + "Vivi", + "Vivia", + "Vivian", + "Viviana", + "Vivianna", + "Vivianne", + "Vivie", + "Vivien", + "Viviene", + "Vivienne", + "Viviyan", + "Vivyan", + "Vivyanne", + "Vonni", + "Vonnie", + "Vonny", + "Vyky", + "Wallie", + "Wallis", + "Walliw", + "Wally", + "Waly", + "Wanda", + "Wandie", + "Wandis", + "Waneta", + "Wanids", + "Wenda", + "Wendeline", + "Wendi", + "Wendie", + "Wendy", + "Wendye", + "Wenona", + "Wenonah", + "Whitney", + "Wileen", + "Wilhelmina", + "Wilhelmine", + "Wilie", + "Willa", + "Willabella", + "Willamina", + "Willetta", + "Willette", + "Willi", + "Willie", + "Willow", + "Willy", + "Willyt", + "Wilma", + "Wilmette", + "Wilona", + "Wilone", + "Wilow", + "Windy", + "Wini", + "Winifred", + "Winna", + "Winnah", + "Winne", + "Winni", + "Winnie", + "Winnifred", + "Winny", + "Winona", + "Winonah", + "Wren", + "Wrennie", + "Wylma", + "Wynn", + "Wynne", + "Wynnie", + "Wynny", + "Xaviera", + "Xena", + "Xenia", + "Xylia", + "Xylina", + "Yalonda", + "Yasmeen", + "Yasmin", + "Yelena", + "Yetta", + "Yettie", + "Yetty", + "Yevette", + "Ynes", + "Ynez", + "Yoko", + "Yolanda", + "Yolande", + "Yolane", + "Yolanthe", + "Yoshi", + "Yoshiko", + "Yovonnda", + "Ysabel", + "Yvette", + "Yvonne", + "Zabrina", + "Zahara", + "Zandra", + "Zaneta", + "Zara", + "Zarah", + "Zaria", + "Zarla", + "Zea", + "Zelda", + "Zelma", + "Zena", + "Zenia", + "Zia", + "Zilvia", + "Zita", + "Zitella", + "Zoe", + "Zola", + "Zonda", + "Zondra", + "Zonnya", + "Zora", + "Zorah", + "Zorana", + "Zorina", + "Zorine", + "Zsa Zsa", + "Zsazsa", + "Zulema", + "Zuzana" +] \ No newline at end of file diff --git a/data/world/last-names.json b/data/world/last-names.json new file mode 100644 index 0000000..4ea9acb --- /dev/null +++ b/data/world/last-names.json @@ -0,0 +1,4948 @@ +[ + "Aaberg", + "Aalst", + "Aara", + "Aaren", + "Aarika", + "Aaron", + "Aaronson", + "Ab", + "Aba", + "Abad", + "Abagael", + "Abagail", + "Abana", + "Abate", + "Abba", + "Abbate", + "Abbe", + "Abbey", + "Abbi", + "Abbie", + "Abbot", + "Abbotsen", + "Abbotson", + "Abbotsun", + "Abbott", + "Abbottson", + "Abby", + "Abbye", + "Abdel", + "Abdella", + "Abdu", + "Abdul", + "Abdulla", + "Abe", + "Abebi", + "Abel", + "Abelard", + "Abell", + "Abercromby", + "Abernathy", + "Abernon", + "Abert", + "Abeu", + "Abey", + "Abie", + "Abigael", + "Abigail", + "Abigale", + "Abijah", + "Abisha", + "Abisia", + "Abixah", + "Abner", + "Aborn", + "Abott", + "Abra", + "Abraham", + "Abrahams", + "Abrahamsen", + "Abrahan", + "Abram", + "Abramo", + "Abrams", + "Abramson", + "Abran", + "Abroms", + "Absa", + "Absalom", + "Abshier", + "Acacia", + "Acalia", + "Accalia", + "Ace", + "Acey", + "Acherman", + "Achilles", + "Achorn", + "Acie", + "Acima", + "Acker", + "Ackerley", + "Ackerman", + "Ackler", + "Ackley", + "Acquah", + "Acus", + "Ad", + "Ada", + "Adabel", + "Adabelle", + "Adachi", + "Adah", + "Adaha", + "Adai", + "Adaiha", + "Adair", + "Adal", + "Adala", + "Adalai", + "Adalard", + "Adalbert", + "Adalheid", + "Adali", + "Adalia", + "Adaliah", + "Adalie", + "Adaline", + "Adall", + "Adallard", + "Adam", + "Adama", + "Adamec", + "Adamek", + "Adamik", + "Adamina", + "Adaminah", + "Adamis", + "Adamo", + "Adamok", + "Adams", + "Adamsen", + "Adamski", + "Adamson", + "Adamsun", + "Adan", + "Adao", + "Adar", + "Adara", + "Adaurd", + "Aday", + "Adda", + "Addam", + "Addi", + "Addia", + "Addie", + "Addiego", + "Addiel", + "Addis", + "Addison", + "Addy", + "Ade", + "Adebayo", + "Adel", + "Adela", + "Adelaida", + "Adelaide", + "Adelaja", + "Adelbert", + "Adele", + "Adelheid", + "Adelia", + "Adelice", + "Adelina", + "Adelind", + "Adeline", + "Adella", + "Adelle", + "Adelpho", + "Adelric", + "Adena", + "Ader", + "Adest", + "Adey", + "Adham", + "Adhamh", + "Adhern", + "Adi", + "Adiana", + "Adiel", + "Adiell", + "Adigun", + "Adila", + "Adim", + "Adin", + "Adina", + "Adine", + "Adis", + "Adkins", + "Adlai", + "Adlar", + "Adlare", + "Adlay", + "Adlee", + "Adlei", + "Adler", + "Adley", + "Adna", + "Adnah", + "Adne", + "Adnopoz", + "Ado", + "Adolf", + "Adolfo", + "Adolph", + "Adolphe", + "Adolpho", + "Adolphus", + "Adon", + "Adonis", + "Adora", + "Adore", + "Adoree", + "Adorl", + "Adorne", + "Adrea", + "Adrell", + "Adria", + "Adriaens", + "Adrial", + "Adrian", + "Adriana", + "Adriane", + "Adrianna", + "Adrianne", + "Adriano", + "Adriel", + "Adriell", + "Adrien", + "Adriena", + "Adriene", + "Adrienne", + "Adur", + "Aekerly", + "Aelber", + "Aenea", + "Aeneas", + "Aeneus", + "Aeniah", + "Aenneea", + "Aeriel", + "Aeriela", + "Aeriell", + "Affer", + "Affra", + "Affrica", + "Afra", + "Africa", + "Africah", + "Afrika", + "Afrikah", + "Afton", + "Ag", + "Agace", + "Agamemnon", + "Agan", + "Agata", + "Agate", + "Agatha", + "Agathe", + "Agathy", + "Agbogla", + "Agee", + "Aggappe", + "Aggappera", + "Aggappora", + "Aggarwal", + "Aggi", + "Aggie", + "Aggri", + "Aggy", + "Agle", + "Agler", + "Agna", + "Agnella", + "Agnes", + "Agnese", + "Agnesse", + "Agneta", + "Agnew", + "Agnola", + "Agostino", + "Agosto", + "Agretha", + "Agripina", + "Agrippina", + "Aguayo", + "Agueda", + "Aguie", + "Aguste", + "Agustin", + "Ahab", + "Aharon", + "Ahasuerus", + "Ahders", + "Ahearn", + "Ahern", + "Ahl", + "Ahlgren", + "Ahmad", + "Ahmar", + "Ahmed", + "Ahola", + "Aholah", + "Aholla", + "Ahoufe", + "Ahouh", + "Ahrendt", + "Ahrens", + "Ahron", + "Aia", + "Aida", + "Aidan", + "Aiden", + "Aiello", + "Aigneis", + "Aiken", + "Aila", + "Ailbert", + "Aile", + "Ailee", + "Aileen", + "Ailene", + "Ailey", + "Aili", + "Ailin", + "Ailina", + "Ailis", + "Ailsa", + "Ailssa", + "Ailsun", + "Ailyn", + "Aime", + "Aimee", + "Aimil", + "Aimo", + "Aindrea", + "Ainslee", + "Ainsley", + "Ainslie", + "Ainsworth", + "Airel", + "Aires", + "Airla", + "Airlee", + "Airlia", + "Airliah", + "Airlie", + "Aisha", + "Ajani", + "Ajax", + "Ajay", + "Ajit", + "Akanke", + "Akel", + "Akela", + "Aker", + "Akerboom", + "Akerley", + "Akers", + "Akeyla", + "Akeylah", + "Akili", + "Akim", + "Akin", + "Akins", + "Akira", + "Aklog", + "Aksel", + "Aksoyn", + "Al", + "Alabaster", + "Alage", + "Alain", + "Alaine", + "Alair", + "Alake", + "Alameda", + "Alan", + "Alana", + "Alanah", + "Aland", + "Alane", + "Alanna", + "Alano", + "Alansen", + "Alanson", + "Alard", + "Alaric", + "Alarice", + "Alarick", + "Alarise", + "Alasdair", + "Alastair", + "Alasteir", + "Alaster", + "Alatea", + "Alathia", + "Alayne", + "Alba", + "Alban", + "Albarran", + "Albemarle", + "Alben", + "Alber", + "Alberic", + "Alberik", + "Albers", + "Albert", + "Alberta", + "Albertina", + "Albertine", + "Alberto", + "Albertson", + "Albie", + "Albin", + "Albina", + "Albion", + "Alboran", + "Albrecht", + "Albric", + "Albright", + "Albur", + "Alburg", + "Alburga", + "Alby", + "Alcina", + "Alcine", + "Alcinia", + "Alcock", + "Alcot", + "Alcott", + "Alcus", + "Alda", + "Aldarcie", + "Aldarcy", + "Aldas", + "Alded", + "Alden", + "Aldercy", + "Alderman", + "Alderson", + "Aldin", + "Aldis", + "Aldo", + "Aldon", + "Aldora", + "Aldos", + "Aldous", + "Aldred", + "Aldredge", + "Aldric", + "Aldrich", + "Aldridge", + "Alduino", + "Aldus", + "Aldwin", + "Aldwon", + "Alec", + "Alecia", + "Aleck", + "Aleda", + "Aleece", + "Aleedis", + "Aleen", + "Aleetha", + "Alegre", + "Alejandra", + "Alejandrina", + "Alejandro", + "Alejo", + "Alejoa", + "Alek", + "Aleksandr", + "Alena", + "Alene", + "Alenson", + "Aleras", + "Aleris", + "Aleron", + "Alesandrini", + "Alessandra", + "Alessandro", + "Aleta", + "Aletha", + "Alethea", + "Alethia", + "Aletta", + "Alex", + "Alexa", + "Alexander", + "Alexandr", + "Alexandra", + "Alexandre", + "Alexandria", + "Alexandrina", + "Alexandro", + "Alexandros", + "Alexei", + "Alexi", + "Alexia", + "Alexina", + "Alexine", + "Alexio", + "Alexis", + "Aley", + "Aleydis", + "Alf", + "Alfeus", + "Alfi", + "Alfie", + "Alfons", + "Alfonse", + "Alfonso", + "Alfonzo", + "Alford", + "Alfred", + "Alfreda", + "Alfredo", + "Alfy", + "Algar", + "Alger", + "Algernon", + "Algie", + "Alguire", + "Algy", + "Ali", + "Alia", + "Aliber", + "Alic", + "Alica", + "Alice", + "Alicea", + "Alicia", + "Alick", + "Alida", + "Alidia", + "Alidis", + "Alidus", + "Alie", + "Alika", + "Alikee", + "Alina", + "Aline", + "Alinna", + "Alis", + "Alisa", + "Alisan", + "Alisander", + "Alisen", + "Alisha", + "Alisia", + "Alison", + "Alissa", + "Alistair", + "Alister", + "Alisun", + "Alita", + "Alitha", + "Alithea", + "Alithia", + "Alitta", + "Alius", + "Alix", + "Aliza", + "Alla", + "Allain", + "Allan", + "Allana", + "Allanson", + "Allard", + "Allare", + "Allayne", + "Allbee", + "Allcot", + "Alleen", + "Allegra", + "Allen", + "Allene", + "Alleras", + "Allerie", + "Alleris", + "Allerus", + "Alley", + "Alleyn", + "Alleyne", + "Alli", + "Allianora", + "Alliber", + "Allie", + "Allin", + "Allina", + "Allis", + "Allisan", + "Allison", + "Allissa", + "Allista", + "Allister", + "Allistir", + "Allix", + "Allmon", + "Allred", + "Allrud", + "Allsopp", + "Allsun", + "Allveta", + "Allwein", + "Allx", + "Ally", + "Allyce", + "Allyn", + "Allys", + "Allyson", + "Alma", + "Almallah", + "Almeda", + "Almeeta", + "Almeida", + "Almena", + "Almeria", + "Almeta", + "Almira", + "Almire", + "Almita", + "Almond", + "Almund", + "Alo", + "Alodee", + "Alodi", + "Alodie", + "Aloin", + "Aloise", + "Aloisia", + "Aloisius", + "Aloke", + "Alon", + "Alonso", + "Alonzo", + "Aloysia", + "Aloysius", + "Alper", + "Alpers", + "Alpert", + "Alphard", + "Alpheus", + "Alphonsa", + "Alphonse", + "Alphonsine", + "Alphonso", + "AlrZc", + "Alric", + "Alrich", + "Alrick", + "Alroi", + "Alroy", + "Also", + "Alston", + "Alsworth", + "Alta", + "Altaf", + "Alten", + "Althea", + "Althee", + "Altheta", + "Altis", + "Altman", + "Alton", + "Aluin", + "Aluino", + "Alurd", + "Alurta", + "Alva", + "Alvan", + "Alvar", + "Alvarez", + "Alver", + "Alvera", + "Alverson", + "Alverta", + "Alves", + "Alveta", + "Alviani", + "Alvie", + "Alvin", + "Alvina", + "Alvinia", + "Alvira", + "Alvis", + "Alvita", + "Alvord", + "Alvy", + "Alwin", + "Alwitt", + "Alwyn", + "Alyce", + "Alyda", + "Alyose", + "Alyosha", + "Alys", + "Alysa", + "Alyse", + "Alysia", + "Alyson", + "Alysoun", + "Alyss", + "Alyssa", + "Alyworth", + "Ama", + "Amabel", + "Amabelle", + "Amabil", + "Amadas", + "Amadeo", + "Amadeus", + "Amadis", + "Amado", + "Amador", + "Amadus", + "Amal", + "Amalbena", + "Amalberga", + "Amalbergas", + "Amalburga", + "Amalea", + "Amalee", + "Amaleta", + "Amalia", + "Amalie", + "Amalita", + "Amalle", + "Aman", + "Amand", + "Amanda", + "Amandi", + "Amandie", + "Amando", + "Amandy", + "Amann", + "Amar", + "Amara", + "Amaral", + "Amaras", + "Amarette", + "Amargo", + "Amari", + "Amarillas", + "Amarillis", + "Amaris", + "Amary", + "Amaryl", + "Amaryllis", + "Amasa", + "Amata", + "Amathist", + "Amathiste", + "Amati", + "Amato", + "Amatruda", + "Amaty", + "Amber", + "Amberly", + "Ambert", + "Ambie", + "Amble", + "Ambler", + "Ambrogino", + "Ambrogio", + "Ambros", + "Ambrosane", + "Ambrose", + "Ambrosi", + "Ambrosia", + "Ambrosine", + "Ambrosio", + "Ambrosius", + "Ambur", + "Amby", + "Ame", + "Amedeo", + "Amelia", + "Amelie", + "Amelina", + "Ameline", + "Amelita", + "Amena", + "Amend", + "Amerigo", + "Amero", + "Amersham", + "Amery", + "Ames", + "Amethist", + "Amethyst", + "Ami", + "Amias", + "Amice", + "Amick", + "Amie", + "Amiel", + "Amieva", + "Amii", + "Amil", + "Amin", + "Aminta", + "Amir", + "Amitie", + "Amity", + "Amling", + "Ammadas", + "Ammadis", + "Ammamaria", + "Ammann", + "Ammon", + "Amoakuh", + "Amor", + "Amora", + "Amoreta", + "Amorete", + "Amorette", + "Amorita", + "Amoritta", + "Amory", + "Amos", + "Amr", + "Amrita", + "Amsden", + "Amund", + "Amy", + "Amyas", + "Amye", + "Amďż˝lie", + "An", + "Ana", + "Anabal", + "Anabel", + "Anabella", + "Anabelle", + "Anagnos", + "Analiese", + "Analise", + "Anallese", + "Anallise", + "Anana", + "Ananna", + "Anastas", + "Anastase", + "Anastasia", + "Anastasie", + "Anastasio", + "Anastasius", + "Anastassia", + "Anastatius", + "Anastice", + "Anastos", + "Anatol", + "Anatola", + "Anatole", + "Anatolio", + "Anatollo", + "Ancalin", + "Ancel", + "Ancelin", + "Anceline", + "Ancell", + "Anchie", + "Ancier", + "Ancilin", + "Andee", + "Andeee", + "Andel", + "Ander", + "Anderea", + "Anderegg", + "Anderer", + "Anders", + "Andersen", + "Anderson", + "Andert", + "Andi", + "Andie", + "Andonis", + "Andra", + "Andrade", + "Andras", + "Andre", + "Andrea", + "Andreana", + "Andreas", + "Andree", + "Andrei", + "Andrej", + "Andrel", + "Andres", + "Andrew", + "Andrews", + "Andrey", + "Andri", + "Andria", + "Andriana", + "Andrien", + "Andriette", + "Andris", + "Andromache", + "Andromada", + "Andromeda", + "Andromede", + "Andros", + "Androw", + "Andrus", + "Andryc", + "Andy", + "Anestassia", + "Anet", + "Anett", + "Anetta", + "Anette", + "Aney", + "Angadreme", + "Angadresma", + "Ange", + "Angel", + "Angela", + "Angele", + "Angeli", + "Angelia", + "Angelica", + "Angelico", + "Angelika", + "Angelina", + "Angeline", + "Angelique", + "Angelis", + "Angelita", + "Angell", + "Angelle", + "Angelo", + "Angi", + "Angie", + "Angil", + "Angle", + "Anglim", + "Anglo", + "Angrist", + "Angus", + "Angy", + "Anh", + "Ania", + "Aniakudo", + "Anica", + "Aniela", + "Anil", + "Anis", + "Anissa", + "Anita", + "Anitra", + "Aniweta", + "Anjali", + "Anjanette", + "Anjela", + "Ankeny", + "Ankney", + "Ann", + "Ann-Marie", + "Anna", + "Anna-Diana", + "Anna-Diane", + "Anna-Maria", + "Annabal", + "Annabel", + "Annabela", + "Annabell", + "Annabella", + "Annabelle", + "Annadiana", + "Annadiane", + "Annalee", + "Annaliese", + "Annalise", + "Annamaria", + "Annamarie", + "Anne", + "Anne-Corinne", + "Anne-Marie", + "Annecorinne", + "Anneliese", + "Annelise", + "Annemarie", + "Annetta", + "Annette", + "Anni", + "Annia", + "Annice", + "Annie", + "Anniken", + "Annis", + "Annissa", + "Annmaria", + "Annmarie", + "Annnora", + "Annora", + "Annorah", + "Annunciata", + "Anny", + "Anora", + "Anse", + "Ansel", + "Ansela", + "Ansell", + "Anselm", + "Anselma", + "Anselme", + "Anselmi", + "Anselmo", + "Ansilma", + "Ansilme", + "Ansley", + "Anson", + "Anstice", + "Anstus", + "Antebi", + "Anthe", + "Anthea", + "Anthia", + "Anthiathia", + "Anthony", + "Antin", + "Antipas", + "Antipus", + "Antoine", + "Antoinetta", + "Antoinette", + "Anton", + "Antone", + "Antonella", + "Antonetta", + "Antoni", + "Antonia", + "Antonie", + "Antonietta", + "Antonin", + "Antonina", + "Antonino", + "Antonio", + "Antonius", + "Antons", + "Antony", + "Antrim", + "Anurag", + "Anuska", + "Any", + "Anya", + "Anyah", + "Anzovin", + "Apfel", + "Apfelstadt", + "Apgar", + "Aphra", + "Aphrodite", + "Apicella", + "Apollo", + "Apollus", + "Apostles", + "Appel", + "Apple", + "Appleby", + "Appledorf", + "Applegate", + "Appleton", + "Appolonia", + "Apps", + "April", + "Aprile", + "Aprilette", + "Apthorp", + "Apul", + "Ara", + "Arabeila", + "Arabel", + "Arabela", + "Arabele", + "Arabella", + "Arabelle", + "Arad", + "Arakawa", + "Araldo", + "Aramanta", + "Aramen", + "Aramenta", + "Araminta", + "Aran", + "Arand", + "Arathorn", + "Arbe", + "Arber", + "Arbuckle", + "Arch", + "Archaimbaud", + "Archambault", + "Archangel", + "Archer", + "Archibald", + "Archibaldo", + "Archibold", + "Archie", + "Archle", + "Archy", + "Ard", + "Arda", + "Ardath", + "Arde", + "Ardeen", + "Ardeha", + "Ardehs", + "Ardel", + "Ardelia", + "Ardelis", + "Ardell", + "Ardella", + "Ardelle", + "Arden", + "Ardene", + "Ardenia", + "Ardeth", + "Ardie", + "Ardin", + "Ardine", + "Ardis", + "Ardisj", + "Ardith", + "Ardme", + "Ardolino", + "Ardra", + "Ardrey", + "Ardussi", + "Ardy", + "Ardyce", + "Ardys", + "Ardyth", + "Arel", + "Arela", + "Arella", + "Arelus", + "Aret", + "Areta", + "Aretha", + "Aretina", + "Aretta", + "Arette", + "Arezzini", + "Argent", + "Argile", + "Argus", + "Argyle", + "Argyres", + "Arhna", + "Ari", + "Aria", + "Ariadne", + "Ariana", + "Ariane", + "Arianie", + "Arianna", + "Arianne", + "Aribold", + "Aric", + "Arica", + "Arick", + "Aridatha", + "Arie", + "Ariel", + "Ariela", + "Ariella", + "Arielle", + "Ariew", + "Arin", + "Ario", + "Arissa", + "Aristotle", + "Arita", + "Arjan", + "Arjun", + "Ark", + "Arlan", + "Arlana", + "Arlee", + "Arleen", + "Arlen", + "Arlena", + "Arlene", + "Arleta", + "Arlette", + "Arley", + "Arleyne", + "Arlie", + "Arliene", + "Arlin", + "Arlina", + "Arlinda", + "Arline", + "Arlo", + "Arlon", + "Arluene", + "Arly", + "Arlyn", + "Arlyne", + "Arlynne", + "Armalda", + "Armalla", + "Armallas", + "Arman", + "Armand", + "Armanda", + "Armando", + "Armbrecht", + "Armbruster", + "Armelda", + "Armil", + "Armilda", + "Armilla", + "Armillas", + "Armillda", + "Armillia", + "Armin", + "Armington", + "Armitage", + "Armond", + "Armstrong", + "Armyn", + "Arnaldo", + "Arnaud", + "Arndt", + "Arne", + "Arnelle", + "Arney", + "Arni", + "Arnie", + "Arno", + "Arnold", + "Arnoldo", + "Arnon", + "Arnst", + "Arnuad", + "Arnulfo", + "Arny", + "Arola", + "Aron", + "Arondel", + "Arondell", + "Aronoff", + "Aronow", + "Aronson", + "Arquit", + "Arratoon", + "Arri", + "Arria", + "Arrio", + "Arron", + "Arst", + "Art", + "Arta", + "Artair", + "Artamas", + "Arte", + "Artema", + "Artemas", + "Artemis", + "Artemisa", + "Artemisia", + "Artemus", + "Arther", + "Arthur", + "Artie", + "Artima", + "Artimas", + "Artina", + "Artur", + "Arturo", + "Artus", + "Arty", + "Aruabea", + "Arun", + "Arundel", + "Arundell", + "Arv", + "Arva", + "Arvad", + "Arvell", + "Arvid", + "Arvie", + "Arvin", + "Arvind", + "Arvo", + "Arvonio", + "Arvy", + "Ary", + "Aryn", + "As", + "Asa", + "Asabi", + "Asante", + "Asaph", + "Asare", + "Aschim", + "Ase", + "Asel", + "Ash", + "Asha", + "Ashbaugh", + "Ashbey", + "Ashby", + "Ashelman", + "Ashely", + "Asher", + "Ashford", + "Ashia", + "Ashien", + "Ashil", + "Ashjian", + "Ashla", + "Ashlan", + "Ashlee", + "Ashleigh", + "Ashlen", + "Ashley", + "Ashli", + "Ashlie", + "Ashlin", + "Ashling", + "Ashly", + "Ashman", + "Ashmead", + "Ashok", + "Ashraf", + "Ashti", + "Ashton", + "Ashwell", + "Ashwin", + "Asia", + "Askari", + "Askwith", + "Aslam", + "Asp", + "Aspa", + "Aspasia", + "Aspia", + "Asquith", + "Assisi", + "Asta", + "Astera", + "Asteria", + "Astor", + "Astra", + "Astraea", + "Astrahan", + "Astrea", + "Astred", + "Astri", + "Astrid", + "Astrix", + "Astto", + "Asuncion", + "Atal", + "Atalanta", + "Atalante", + "Atalanti", + "Atalaya", + "Atalayah", + "Atalee", + "Ataliah", + "Atalie", + "Atalya", + "Atcliffe", + "Athal", + "Athalee", + "Athalia", + "Athalie", + "Athalla", + "Athallia", + "Athelstan", + "Athena", + "Athene", + "Athenian", + "Athey", + "Athiste", + "Atiana", + "Atkins", + "Atkinson", + "Atlanta", + "Atlante", + "Atlas", + "Atlee", + "Atonsah", + "Atrice", + "Atronna", + "Attah", + "Attalanta", + "Attalie", + "Attenborough", + "Attenweiler", + "Atterbury", + "Atthia", + "Attlee", + "Attwood", + "Atul", + "Atwater", + "Atwekk", + "Atwood", + "Atworth", + "Au", + "Aubarta", + "Aube", + "Auberbach", + "Auberon", + "Aubert", + "Auberta", + "Aubigny", + "Aubin", + "Aubine", + "Aubree", + "Aubreir", + "Aubrette", + "Aubrey", + "Aubrie", + "Aubry", + "Auburn", + "Auburta", + "Aubyn", + "Audette", + "Audi", + "Audie", + "Audley", + "Audly", + "Audra", + "Audras", + "Audre", + "Audres", + "Audrey", + "Audri", + "Audrie", + "Audris", + "Audrit", + "Audry", + "Audrye", + "Audsley", + "Audun", + "Audwen", + "Audwin", + "Audy", + "Auerbach", + "Aufmann", + "Augie", + "August", + "Augusta", + "Auguste", + "Augustin", + "Augustina", + "Augustine", + "Augusto", + "Augustus", + "Augy", + "Aulea", + "Auliffe", + "Aun", + "Aundrea", + "Aunson", + "Aura", + "Aurea", + "Aurel", + "Aurelea", + "Aurelia", + "Aurelie", + "Aurelio", + "Aurelius", + "Auria", + "Auric", + "Aurie", + "Aurilia", + "Aurita", + "Aurlie", + "Auroora", + "Aurora", + "Aurore", + "Aurthur", + "Ause", + "Austen", + "Austin", + "Austina", + "Austine", + "Auston", + "Australia", + "Austreng", + "Autrey", + "Autry", + "Autum", + "Autumn", + "Auvil", + "Av", + "Ava", + "Avan", + "Avaria", + "Ave", + "Avelin", + "Aveline", + "Avera", + "Averell", + "Averi", + "Averil", + "Averill", + "Averir", + "Avery", + "Averyl", + "Avi", + "Avictor", + "Avie", + "Avigdor", + "Avilla", + "Avis", + "Avitzur", + "Aviv", + "Aviva", + "Avivah", + "Avner", + "Avra", + "Avraham", + "Avram", + "Avril", + "Avrit", + "Avrom", + "Avron", + "Avruch", + "Awad", + "Ax", + "Axe", + "Axel", + "Aylmar", + "Aylmer", + "Aylsworth", + "Aylward", + "Aymer", + "Ayn", + "Aynat", + "Ayo", + "Ayres", + "Azal", + "Azalea", + "Azaleah", + "Azar", + "Azarcon", + "Azaria", + "Azarria", + "Azelea", + "Azeria", + "Aziza", + "Azpurua", + "Azral", + "Azriel", + "Baal", + "Baalbeer", + "Baalman", + "Bab", + "Babara", + "Babb", + "Babbette", + "Babbie", + "Babby", + "Babcock", + "Babette", + "Babita", + "Babs", + "Bac", + "Bacchus", + "Bach", + "Bachman", + "Backer", + "Backler", + "Bacon", + "Badger", + "Badr", + "Baecher", + "Bael", + "Baelbeer", + "Baer", + "Baerl", + "Baerman", + "Baese", + "Bagger", + "Baggett", + "Baggott", + "Baggs", + "Bagley", + "Bahner", + "Bahr", + "Baiel", + "Bail", + "Bailar", + "Bailey", + "Bailie", + "Baillie", + "Baillieu", + "Baily", + "Bain", + "Bainbridge", + "Bainbrudge", + "Bainter", + "Baird", + "Baiss", + "Bajaj", + "Bak", + "Bakeman", + "Bakemeier", + "Baker", + "Bakerman", + "Bakki", + "Bal", + "Bala", + "Balas", + "Balbinder", + "Balbur", + "Balcer", + "Balch", + "Balcke", + "Bald", + "Baldridge", + "Balduin", + "Baldwin", + "Bale", + "Baler", + "Balf", + "Balfore", + "Balfour", + "Balkin", + "Ball", + "Ballard", + "Balliett", + "Balling", + "Ballinger", + "Balliol", + "Ballman", + "Ballou", + "Balmuth", + "Balough", + "Balsam", + "Balthasar", + "Balthazar", + "Bamberger", + "Bambi", + "Bambie", + "Bamby", + "Bamford", + "Ban", + "Bancroft", + "Bandeen", + "Bander", + "Bandler", + "Bandur", + "Banebrudge", + "Banerjee", + "Bang", + "Bank", + "Banks", + "Banky", + "Banna", + "Bannasch", + "Bannerman", + "Bannister", + "Bannon", + "Banquer", + "Banwell", + "Baptist", + "Baptista", + "Baptiste", + "Baptlsta", + "Bar", + "Bara", + "Barabas", + "Barabbas", + "Baram", + "Baras", + "Barayon", + "Barb", + "Barbabas", + "Barbabra", + "Barbara", + "Barbara-Anne", + "Barbaraanne", + "Barbarese", + "Barbaresi", + "Barbe", + "Barbee", + "Barber", + "Barbette", + "Barbey", + "Barbi", + "Barbie", + "Barbour", + "Barboza", + "Barbra", + "Barbur", + "Barbuto", + "Barby", + "Barcellona", + "Barclay", + "Barcot", + "Barcroft", + "Barcus", + "Bard", + "Barde", + "Barden", + "Bardo", + "Barfuss", + "Barger", + "Bari", + "Barimah", + "Barina", + "Barker", + "Barkley", + "Barling", + "Barlow", + "Barmen", + "Barn", + "Barna", + "Barnaba", + "Barnabas", + "Barnabe", + "Barnaby", + "Barnard", + "Barncard", + "Barnebas", + "Barnes", + "Barnet", + "Barnett", + "Barney", + "Barnie", + "Barnum", + "Barny", + "Barolet", + "Baron", + "Barr", + "Barra", + "Barrada", + "Barram", + "Barraza", + "Barren", + "Barret", + "Barrett", + "Barri", + "Barrie", + "Barrington", + "Barris", + "Barron", + "Barrow", + "Barrus", + "Barry", + "Barsky", + "Barstow", + "Bart", + "Barta", + "Bartel", + "Barth", + "Barthel", + "Barthelemy", + "Barthol", + "Barthold", + "Bartholemy", + "Bartholomeo", + "Bartholomeus", + "Bartholomew", + "Bartie", + "Bartko", + "Bartle", + "Bartlet", + "Bartlett", + "Bartley", + "Bartolemo", + "Bartolome", + "Bartolomeo", + "Barton", + "Bartosch", + "Bartram", + "Barty", + "Baruch", + "Barvick", + "Bary", + "Baryram", + "Bascio", + "Bascomb", + "Base", + "Baseler", + "Basham", + "Bashee", + "Bashemath", + "Bashemeth", + "Bashuk", + "Basia", + "Basil", + "Basile", + "Basilio", + "Basilius", + "Basir", + "Baskett", + "Bass", + "Basset", + "Bassett", + "Basso", + "Bast", + "Bastian", + "Bastien", + "Bat", + "Batchelor", + "Bate", + "Baten", + "Bates", + "Batha", + "Bathelda", + "Bathesda", + "Bathilda", + "Batholomew", + "Bathsheb", + "Bathsheba", + "Bathsheeb", + "Bathulda", + "Batish", + "Batista", + "Batory", + "Batruk", + "Batsheva", + "Battat", + "Battista", + "Battiste", + "Batty", + "Baudelaire", + "Baudin", + "Baudoin", + "Bauer", + "Baugh", + "Baum", + "Baumann", + "Baumbaugh", + "Baun", + "Bausch", + "Bauske", + "Bautista", + "Bautram", + "Bax", + "Baxie", + "Baxter", + "Baxy", + "Bay", + "Bayard", + "Bayer", + "Bayless", + "Baylor", + "Bayly", + "Baynebridge", + "Bazar", + "Bazil", + "Bazluke", + "Bea", + "Beach", + "Beacham", + "Beal", + "Beale", + "Beall", + "Bealle", + "Bean", + "Beane", + "Beaner", + "Bear", + "Bearce", + "Beard", + "Beare", + "Bearnard", + "Beasley", + "Beaston", + "Beata", + "Beatrice", + "Beatrisa", + "Beatrix", + "Beatriz", + "Beattie", + "Beatty", + "Beau", + "Beauchamp", + "Beaudoin", + "Beaufert", + "Beaufort", + "Beaulieu", + "Beaumont", + "Beauregard", + "Beauvais", + "Beaver", + "Bebe", + "Beberg", + "Becca", + "Bechler", + "Becht", + "Beck", + "Becka", + "Becker", + "Beckerman", + "Becket", + "Beckett", + "Becki", + "Beckie", + "Beckman", + "Becky", + "Bedad", + "Bedelia", + "Bedell", + "Bedwell", + "Bee", + "Beebe", + "Beeck", + "Beedon", + "Beekman", + "Beera", + "Beesley", + "Beeson", + "Beetner", + "Beffrey", + "Bega", + "Begga", + "Beghtol", + "Behah", + "Behka", + "Behl", + "Behlau", + "Behlke", + "Behm", + "Behn", + "Behnken", + "Behre", + "Behrens", + "Beichner", + "Beilul", + "Bein", + "Beisel", + "Beitch", + "Beitnes", + "Beitris", + "Beitz", + "Beka", + "Bekah", + "Bekelja", + "Beker", + "Bekha", + "Bekki", + "Bel", + "Bela", + "Belak", + "Belamy", + "Belanger", + "Belayneh", + "Belcher", + "Belda", + "Belden", + "Belding", + "Belen", + "Belford", + "Belia", + "Belicia", + "Belier", + "Belinda", + "Belita", + "Bell", + "Bella", + "Bellamy", + "Bellanca", + "Bellaude", + "Bellda", + "Belldame", + "Belldas", + "Belle", + "Beller", + "Bellew", + "Bellina", + "Bellis", + "Bello", + "Belloir", + "Belmonte", + "Belshin", + "Belsky", + "Belter", + "Beltran", + "Belva", + "Belvia", + "Ben", + "Bena", + "Bencion", + "Benco", + "Bender", + "Bendick", + "Bendicta", + "Bendicty", + "Bendite", + "Bendix", + "Benedetta", + "Benedetto", + "Benedic", + "Benedick", + "Benedict", + "Benedicta", + "Benedicto", + "Benedikt", + "Benedikta", + "Benedix", + "Benenson", + "Benetta", + "Benge", + "Bengt", + "Benia", + "Beniamino", + "Benil", + "Benilda", + "Benildas", + "Benildis", + "Benioff", + "Benis", + "Benisch", + "Benita", + "Benito", + "Benjamen", + "Benjamin", + "Benji", + "Benjie", + "Benjy", + "Benkley", + "Benn", + "Bennet", + "Bennett", + "Benni", + "Bennie", + "Bennink", + "Bennion", + "Bennir", + "Benny", + "Benoit", + "Benoite", + "Bensen", + "Bensky", + "Benson", + "Bent", + "Bentlee", + "Bentley", + "Bently", + "Benton", + "Benyamin", + "Benzel", + "Beora", + "Beore", + "Ber", + "Berard", + "Berardo", + "Berck", + "Berenice", + "Beret", + "Berey", + "Berfield", + "Berg", + "Berga", + "Bergeman", + "Bergen", + "Berger", + "Bergerac", + "Bergeron", + "Bergess", + "Berget", + "Bergh", + "Berghoff", + "Bergin", + "Berglund", + "Bergman", + "Bergmann", + "Bergmans", + "Bergquist", + "Bergren", + "Bergstein", + "Bergstrom", + "Bergwall", + "Berhley", + "Berk", + "Berke", + "Berkeley", + "Berkie", + "Berkin", + "Berkley", + "Berkly", + "Berkman", + "Berkow", + "Berkshire", + "Berky", + "Berl", + "Berlauda", + "Berlin", + "Berlinda", + "Berliner", + "Berlyn", + "Berman", + "Bern", + "Berna", + "Bernadene", + "Bernadette", + "Bernadina", + "Bernadine", + "Bernard", + "Bernardi", + "Bernardina", + "Bernardine", + "Bernardo", + "Bernarr", + "Bernat", + "Berne", + "Bernelle", + "Berner", + "Berners", + "Berneta", + "Bernete", + "Bernetta", + "Bernette", + "Bernhard", + "Berni", + "Bernice", + "Bernie", + "Bernita", + "Bernj", + "Berns", + "Bernstein", + "Bernt", + "Berny", + "Berri", + "Berrie", + "Berriman", + "Berry", + "Berstine", + "Bert", + "Berta", + "Bertasi", + "Berte", + "Bertelli", + "Bertero", + "Bertha", + "Berthe", + "Berthold", + "Berthoud", + "Berti", + "Bertie", + "Bertila", + "Bertilla", + "Bertina", + "Bertine", + "Bertle", + "Bertold", + "Bertolde", + "Berton", + "Bertram", + "Bertrand", + "Bertrando", + "Bertsche", + "Berty", + "Berwick", + "Beryl", + "Beryle", + "Beshore", + "Besnard", + "Bess", + "Besse", + "Bessie", + "Bessy", + "Best", + "Beth", + "Bethanne", + "Bethany", + "Bethel", + "Bethena", + "Bethesda", + "Bethesde", + "Bethezel", + "Bethina", + "Betsey", + "Betsy", + "Betta", + "Bette", + "Bette-Ann", + "Betteann", + "Betteanne", + "Bettencourt", + "Betthel", + "Betthezel", + "Betthezul", + "Betti", + "Bettina", + "Bettine", + "Betty", + "Bettye", + "Bettzel", + "Betz", + "Beulah", + "Beuthel", + "Beutler", + "Beutner", + "Bev", + "Bevan", + "Bevash", + "Bever", + "Beverie", + "Beverle", + "Beverlee", + "Beverley", + "Beverlie", + "Beverly", + "Bevers", + "Bevin", + "Bevis", + "Bevon", + "Bevus", + "Bevvy", + "Beyer", + "Bezanson", + "Bhatt", + "Bhayani", + "Biagi", + "Biagio", + "Biamonte", + "Bianca", + "Biancha", + "Bianchi", + "Bianka", + "Bibbie", + "Bibby", + "Bibbye", + "Bibeau", + "Bibi", + "Bible", + "Bick", + "Bickart", + "Bicknell", + "Biddick", + "Biddie", + "Biddle", + "Biddy", + "Bidget", + "Bidle", + "Biebel", + "Biegel", + "Bierman", + "Biernat", + "Bigelow", + "Bigford", + "Bigg", + "Biggs", + "Bigler", + "Bigner", + "Bigod", + "Bigot", + "Bik", + "Bikales", + "Bil", + "Bilbe", + "Bilek", + "Biles", + "Bili", + "Bilicki", + "Bill", + "Billat", + "Bille", + "Billen", + "Billi", + "Billie", + "Billmyre", + "Bills", + "Billy", + "Billye", + "Bilow", + "Bilski", + "Bina", + "Binah", + "Bindman", + "Binetta", + "Binette", + "Bing", + "Bink", + "Binky", + "Binni", + "Binnie", + "Binnings", + "Binny", + "Biondo", + "Birch", + "Birchard", + "Birck", + "Bird", + "Birdella", + "Birdie", + "Birdt", + "Birecree", + "Birgit", + "Birgitta", + "Birk", + "Birkett", + "Birkle", + "Birkner", + "Birmingham", + "Biron", + "Bish", + "Bishop", + "Bissell", + "Bisset", + "Bithia", + "Bittencourt", + "Bitthia", + "Bittner", + "Bivins", + "Bixby", + "Bixler", + "Bjork", + "Bjorn", + "Black", + "Blackburn", + "Blackington", + "Blackman", + "Blackmore", + "Blackmun", + "Blackstock", + "Blackwell", + "Blader", + "Blain", + "Blaine", + "Blainey", + "Blair", + "Blaire", + "Blaise", + "Blake", + "Blakelee", + "Blakeley", + "Blakely", + "Blalock", + "Blanc", + "Blanca", + "Blanch", + "Blancha", + "Blanchard", + "Blanche", + "Blanchette", + "Bland", + "Blandina", + "Blanding", + "Blane", + "Blank", + "Blanka", + "Blankenship", + "Blas", + "Blase", + "Blaseio", + "Blasien", + "Blasius", + "Blatman", + "Blatt", + "Blau", + "Blayne", + "Blayze", + "Blaze", + "Bledsoe", + "Bleier", + "Blen", + "Blessington", + "Blight", + "Blim", + "Blinni", + "Blinnie", + "Blinny", + "Bliss", + "Blisse", + "Blithe", + "Bloch", + "Block", + "Blockus", + "Blodget", + "Blodgett", + "Bloem", + "Blondell", + "Blondelle", + "Blondie", + "Blondy", + "Blood", + "Bloom", + "Bloomer", + "Blossom", + "Blount", + "Bloxberg", + "Bluefarb", + "Bluefield", + "Bluh", + "Bluhm", + "Blum", + "Bluma", + "Blumenfeld", + "Blumenthal", + "Blunk", + "Blunt", + "Blus", + "Blynn", + "Blythe", + "Bo", + "Boak", + "Boar", + "Boardman", + "Boarer", + "Boaten", + "Boatwright", + "Bob", + "Bobbe", + "Bobbee", + "Bobbette", + "Bobbi", + "Bobbie", + "Bobby", + "Bobbye", + "Bobette", + "Bobina", + "Bobine", + "Bobinette", + "Bobker", + "Bobseine", + "Bock", + "Bocock", + "Bodi", + "Bodkin", + "Bodnar", + "Bodrogi", + "Bodwell", + "Body", + "Boehike", + "Boehmer", + "Boeke", + "Boelter", + "Boesch", + "Boeschen", + "Boff", + "Boffa", + "Bogart", + "Bogey", + "Boggers", + "Boggs", + "Bogie", + "Bogoch", + "Bogosian", + "Bogusz", + "Bohannon", + "Bohaty", + "Bohi", + "Bohlen", + "Bohlin", + "Bohman", + "Bohner", + "Bohon", + "Bohrer", + "Bohs", + "Bohun", + "Boice", + "Boigie", + "Boiney", + "Bois", + "Bolan", + "Boland", + "Bolanger", + "Bolen", + "Boles", + "Boleslaw", + "Boleyn", + "Bolger", + "Bolitho", + "Bollay", + "Bollen", + "Bolling", + "Bollinger", + "Bolme", + "Bolt", + "Bolte", + "Bolten", + "Bolton", + "Bomke", + "Bonacci", + "Bonaparte", + "Bonar", + "Bond", + "Bondie", + "Bondon", + "Bondy", + "Bone", + "Boni", + "Boniface", + "Bonilla", + "Bonina", + "Bonine", + "Bonis", + "Bonita", + "Bonn", + "Bonne", + "Bonneau", + "Bonnee", + "Bonnell", + "Bonner", + "Bonnes", + "Bonnette", + "Bonney", + "Bonni", + "Bonnibelle", + "Bonnice", + "Bonnie", + "Bonns", + "Bonny", + "Bonucci", + "Booker", + "Booma", + "Boone", + "Boonie", + "Boony", + "Boor", + "Boorer", + "Boorman", + "Boot", + "Boote", + "Booth", + "Boothe", + "Boothman", + "Booze", + "Bopp", + "Bor", + "Bora", + "Borchers", + "Borchert", + "Bord", + "Borden", + "Bordie", + "Bordiuk", + "Bordy", + "Bore", + "Borek", + "Borer", + "Bores", + "Borg", + "Borgeson", + "Boris", + "Bork", + "Borlase", + "Borlow", + "Borman", + "Born", + "Bornie", + "Bornstein", + "Borras", + "Borrell", + "Borreri", + "Borries", + "Borroff", + "Borszcz", + "Bortman", + "Bortz", + "Boru", + "Bosch", + "Bose", + "Boser", + "Bosson", + "Bostow", + "Boswall", + "Boswell", + "Botnick", + "Botsford", + "Bottali", + "Botti", + "Botzow", + "Bouchard", + "Boucher", + "Bouchier", + "Boudreaux", + "Bough", + "Boulanger", + "Bouldon", + "Bouley", + "Bound", + "Bounds", + "Bourgeois", + "Bourke", + "Bourn", + "Bourne", + "Bourque", + "Boutis", + "Bouton", + "Bouzoun", + "Bove", + "Bovill", + "Bow", + "Bowden", + "Bowe", + "Bowen", + "Bower", + "Bowerman", + "Bowers", + "Bowes", + "Bowie", + "Bowlds", + "Bowler", + "Bowles", + "Bowman", + "Bowne", + "Bowra", + "Bowrah", + "Bowyer", + "Box", + "Boy", + "Boyce", + "Boycey", + "Boycie", + "Boyd", + "Boyden", + "Boyer", + "Boyes", + "Boykins", + "Boylan", + "Boylston", + "Boynton", + "Boys", + "Boyse", + "Boyt", + "Bozovich", + "Bozuwa", + "Braasch", + "Brabazon", + "Braca", + "Bracci", + "Brace", + "Brackely", + "Brackett", + "Brad", + "Bradan", + "Brade", + "Braden", + "Bradeord", + "Brader", + "Bradford", + "Bradlee", + "Bradleigh", + "Bradley", + "Bradly", + "Bradman", + "Bradney", + "Bradshaw", + "Bradski", + "Bradstreet", + "Bradway", + "Bradwell", + "Brady", + "Braeunig", + "Brag", + "Brahear", + "Brainard", + "Bram", + "Bramwell", + "Bran", + "Brana", + "Branca", + "Branch", + "Brand", + "Brandais", + "Brande", + "Brandea", + "Branden", + "Brandenburg", + "Brander", + "Brandes", + "Brandi", + "Brandice", + "Brandie", + "Brandise", + "Brandon", + "Brandt", + "Brandtr", + "Brandwein", + "Brandy", + "Brandyn", + "Branen", + "Branham", + "Brannon", + "Branscum", + "Brant", + "Brantley", + "Brasca", + "Brass", + "Braswell", + "Brathwaite", + "Bratton", + "Braun", + "Braunstein", + "Brause", + "Bravar", + "Bravin", + "Brawley", + "Brawner", + "Bray", + "Braynard", + "Brazee", + "Breana", + "Breanne", + "Brear", + "Breban", + "Brebner", + "Brecher", + "Brechtel", + "Bred", + "Bree", + "Breech", + "Breed", + "Breen", + "Breena", + "Breeze", + "Breger", + "Brelje", + "Bremble", + "Bremen", + "Bremer", + "Bremser", + "Bren", + "Brena", + "Brenan", + "Brenda", + "Brendan", + "Brenden", + "Brendin", + "Brendis", + "Brendon", + "Brenk", + "Brenn", + "Brenna", + "Brennan", + "Brennen", + "Brenner", + "Brent", + "Brenton", + "Brentt", + "Brenza", + "Bresee", + "Breskin", + "Brest", + "Bret", + "Brett", + "Brew", + "Brewer", + "Brewster", + "Brey", + "Brezin", + "Bria", + "Brian", + "Briana", + "Brianna", + "Brianne", + "Briano", + "Briant", + "Brice", + "Brick", + "Bricker", + "Bride", + "Bridge", + "Bridges", + "Bridget", + "Bridgette", + "Bridgid", + "Bridie", + "Bridwell", + "Brie", + "Brien", + "Brier", + "Brieta", + "Brietta", + "Brig", + "Brigette", + "Brigg", + "Briggs", + "Brigham", + "Bright", + "Brightman", + "Brighton", + "Brigid", + "Brigida", + "Brigit", + "Brigitta", + "Brigitte", + "Brill", + "Brina", + "Brindell", + "Brindle", + "Brine", + "Briney", + "Bringhurst", + "Brink", + "Brinkema", + "Brinn", + "Brinna", + "Brinson", + "Briny", + "Brion", + "Briscoe", + "Bristow", + "Brit", + "Brita", + "Britney", + "Britni", + "Britt", + "Britta", + "Brittain", + "Brittan", + "Brittaney", + "Brittani", + "Brittany", + "Britte", + "Britteny", + "Brittne", + "Brittnee", + "Brittney", + "Brittni", + "Britton", + "Brnaba", + "Brnaby", + "Broadbent", + "Brock", + "Brockie", + "Brocklin", + "Brockwell", + "Brocky", + "Brod", + "Broddie", + "Broddy", + "Brodench", + "Broder", + "Broderic", + "Broderick", + "Brodeur", + "Brodie", + "Brodsky", + "Brody", + "Broeder", + "Broek", + "Broeker", + "Brogle", + "Broida", + "Brok", + "Brom", + "Bromleigh", + "Bromley", + "Bron", + "Bronder", + "Bronez", + "Bronk", + "Bronnie", + "Bronny", + "Bronson", + "Bronwen", + "Bronwyn", + "Brook", + "Brooke", + "Brookes", + "Brookhouse", + "Brooking", + "Brookner", + "Brooks", + "Broome", + "Brose", + "Brosine", + "Brost", + "Brosy", + "Brote", + "Brothers", + "Brotherson", + "Brott", + "Brottman", + "Broucek", + "Brout", + "Brouwer", + "Brower", + "Brown", + "Browne", + "Browning", + "Brownley", + "Brownson", + "Brozak", + "Brubaker", + "Bruce", + "Brucie", + "Bruckner", + "Bruell", + "Brufsky", + "Bruis", + "Brunell", + "Brunella", + "Brunelle", + "Bruner", + "Brunhild", + "Brunhilda", + "Brunhilde", + "Bruni", + "Bruning", + "Brunk", + "Brunn", + "Bruno", + "Bruns", + "Bruyn", + "Bryan", + "Bryana", + "Bryant", + "Bryanty", + "Bryce", + "Bryn", + "Bryna", + "Bryner", + "Brynn", + "Brynna", + "Brynne", + "Bryon", + "Buatti", + "Bubalo", + "Bubb", + "Bucella", + "Buchalter", + "Buchanan", + "Buchbinder", + "Bucher", + "Buchheim", + "Buck", + "Buckden", + "Buckels", + "Buckie", + "Buckingham", + "Buckler", + "Buckley", + "Bucky", + "Bud", + "Budd", + "Budde", + "Buddie", + "Budding", + "Buddy", + "Buderus", + "Budge", + "Budwig", + "Budworth", + "Buehler", + "Buehrer", + "Buell", + "Buerger", + "Bueschel", + "Buff", + "Buffo", + "Buffum", + "Buffy", + "Buford", + "Bugbee", + "Buhler", + "Bui", + "Buine", + "Buiron", + "Buke", + "Bull", + "Bullard", + "Bullen", + "Buller", + "Bulley", + "Bullion", + "Bullis", + "Bullivant", + "Bullock", + "Bullough", + "Bully", + "Bultman", + "Bum", + "Bumgardner", + "Buna", + "Bunce", + "Bunch", + "Bunde", + "Bunder", + "Bundy", + "Bunker", + "Bunni", + "Bunnie", + "Bunns", + "Bunny", + "Bunow", + "Bunting", + "Buonomo", + "Buote", + "Burack", + "Burbank", + "Burch", + "Burchett", + "Burck", + "Burd", + "Burdelle", + "Burdett", + "Burford", + "Burg", + "Burgener", + "Burger", + "Burgess", + "Burget", + "Burgwell", + "Burhans", + "Burk", + "Burke", + "Burkhard", + "Burkhardt", + "Burkhart", + "Burkitt", + "Burkle", + "Burkley", + "Burl", + "Burleigh", + "Burley", + "Burlie", + "Burman", + "Burn", + "Burnaby", + "Burnard", + "Burne", + "Burner", + "Burnett", + "Burney", + "Burnham", + "Burnie", + "Burnight", + "Burnley", + "Burns", + "Burnsed", + "Burnside", + "Burny", + "Buroker", + "Burr", + "Burra", + "Burrell", + "Burrill", + "Burris", + "Burroughs", + "Burrow", + "Burrows", + "Burrton", + "Burrus", + "Burt", + "Burta", + "Burtie", + "Burtis", + "Burton", + "Burty", + "Burwell", + "Bury", + "Busby", + "Busch", + "Buschi", + "Buseck", + "Busey", + "Bush", + "Bushey", + "Bushore", + "Bushweller", + "Busiek", + "Buskirk", + "Buskus", + "Bussey", + "Bussy", + "Bust", + "Butch", + "Butcher", + "Butler", + "Butta", + "Buttaro", + "Butte", + "Butterfield", + "Butterworth", + "Button", + "Buxton", + "Buyer", + "Buyers", + "Buyse", + "Buzz", + "Buzzell", + "Byers", + "Byler", + "Byram", + "Byran", + "Byrann", + "Byrd", + "Byrdie", + "Byrle", + "Byrn", + "Byrne", + "Byrom", + "Byron", + "Bysshe", + "Bywaters", + "Bywoods", + "Cacia", + "Cacie", + "Cacilia", + "Cacilie", + "Cacka", + "Cad", + "Cadal", + "Caddaric", + "Caddric", + "Cade", + "Cadel", + "Cadell", + "Cadman", + "Cadmann", + "Cadmar", + "Cadmarr", + "Caesar", + "Caesaria", + "Caffrey", + "Cagle", + "Cahan", + "Cahilly", + "Cahn", + "Cahra", + "Cai", + "Caia", + "Caiaphas", + "Cailean", + "Cailly", + "Cain", + "Caine", + "Caines", + "Cairistiona", + "Cairns", + "Caitlin", + "Caitrin", + "Cal", + "Calabrese", + "Calabresi", + "Calan", + "Calandra", + "Calandria", + "Calbert", + "Caldeira", + "Calder", + "Caldera", + "Calderon", + "Caldwell", + "Cale", + "Caleb", + "Calen", + "Calendra", + "Calendre", + "Calesta", + "Calhoun", + "Calia", + "Calica", + "Calida", + "Calie", + "Calisa", + "Calise", + "Calista", + "Call", + "Calla", + "Callahan", + "Callan", + "Callas", + "Calle", + "Callean", + "Callery", + "Calley", + "Calli", + "Callida", + "Callie", + "Callista", + "Calloway", + "Callum", + "Cally", + "Calmas", + "Calondra", + "Calore", + "Calv", + "Calva", + "Calvano", + "Calvert", + "Calvin", + "Calvina", + "Calvinna", + "Calvo", + "Calypso", + "Calysta", + "Cam", + "Camala", + "Camarata", + "Camden", + "Camel", + "Camella", + "Camellia", + "Cameron", + "Camey", + "Camfort", + "Cami", + "Camila", + "Camile", + "Camilia", + "Camilla", + "Camille", + "Camilo", + "Camm", + "Cammi", + "Cammie", + "Cammy", + "Camp", + "Campagna", + "Campball", + "Campbell", + "Campman", + "Campney", + "Campos", + "Campy", + "Camus", + "Can", + "Canada", + "Canale", + "Cand", + "Candace", + "Candi", + "Candice", + "Candida", + "Candide", + "Candie", + "Candis", + "Candless", + "Candra", + "Candy", + "Candyce", + "Caneghem", + "Canfield", + "Canica", + "Canice", + "Caniff", + "Cann", + "Cannell", + "Cannice", + "Canning", + "Cannon", + "Canon", + "Canotas", + "Canter", + "Cantlon", + "Cantone", + "Cantu", + "Canty", + "Canute", + "Capello", + "Caplan", + "Capon", + "Capone", + "Capp", + "Cappella", + "Cappello", + "Capps", + "Caprice", + "Capriola", + "Caputo", + "Caputto", + "Capwell", + "Car", + "Cara", + "Caralie", + "Caras", + "Caravette", + "Caraviello", + "Carberry", + "Carbo", + "Carbone", + "Carboni", + "Carbrey", + "Carce", + "Card", + "Carder", + "Cardew", + "Cardie", + "Cardinal", + "Cardon", + "Cardwell", + "Care", + "Careaga", + "Caren", + "Carena", + "Caresa", + "Caressa", + "Caresse", + "Carew", + "Carey", + "Cargian", + "Carhart", + "Cari", + "Caria", + "Carie", + "Caril", + "Carilla", + "Carilyn", + "Carin", + "Carina", + "Carine", + "Cariotta", + "Carisa", + "Carissa", + "Carita", + "Caritta", + "Carl", + "Carla", + "Carlee", + "Carleen", + "Carlen", + "Carlene", + "Carleton", + "Carley", + "Carli", + "Carlick", + "Carlie", + "Carlile", + "Carlin", + "Carlina", + "Carline", + "Carling", + "Carlisle", + "Carlita", + "Carlo", + "Carlock", + "Carlos", + "Carlota", + "Carlotta", + "Carlson", + "Carlstrom", + "Carlton", + "Carly", + "Carlye", + "Carlyle", + "Carlyn", + "Carlynn", + "Carlynne", + "Carma", + "Carman", + "Carmel", + "Carmela", + "Carmelia", + "Carmelina", + "Carmelita", + "Carmella", + "Carmelle", + "Carmelo", + "Carmen", + "Carmena", + "Carmencita", + "Carmina", + "Carmine", + "Carmita", + "Carmon", + "Carn", + "Carnahan", + "Carnay", + "Carnes", + "Carney", + "Carny", + "Caro", + "Carol", + "Carol-Jean", + "Carola", + "Carolan", + "Carolann", + "Carole", + "Carolee", + "Carolin", + "Carolina", + "Caroline", + "Carolle", + "Carolus", + "Carolyn", + "Carolyne", + "Carolynn", + "Carolynne", + "Caron", + "Carothers", + "Carpenter", + "Carper", + "Carpet", + "Carpio", + "Carr", + "Carree", + "Carrel", + "Carrelli", + "Carrew", + "Carri", + "Carrick", + "Carrie", + "Carrillo", + "Carrington", + "Carrissa", + "Carrnan", + "Carrol", + "Carroll", + "Carry", + "Carson", + "Cart", + "Cartan", + "Carter", + "Carthy", + "Cartie", + "Cartwell", + "Cartwright", + "Caruso", + "Carver", + "Carvey", + "Cary", + "Caryl", + "Caryn", + "Cas", + "Casabonne", + "Casady", + "Casaleggio", + "Casandra", + "Casanova", + "Casar", + "Casavant", + "Case", + "Casey", + "Cash", + "Casi", + "Casia", + "Casie", + "Casilda", + "Casilde", + "Casimir", + "Casimire", + "Casmey", + "Caspar", + "Casper", + "Cass", + "Cassady", + "Cassandra", + "Cassandre", + "Cassandry", + "Cassaundra", + "Cassell", + "Cassella", + "Cassey", + "Cassi", + "Cassiani", + "Cassidy", + "Cassie", + "Cassil", + "Cassilda", + "Cassius", + "Cassondra", + "Cassy", + "Casta", + "Castara", + "Casteel", + "Castera", + "Castillo", + "Castle", + "Castor", + "Castora", + "Castorina", + "Castra", + "Castro", + "Caswell", + "Cataldo", + "Catarina", + "Cate", + "Caterina", + "Cates", + "Cath", + "Catha", + "Catharina", + "Catharine", + "Cathe", + "Cathee", + "Catherin", + "Catherina", + "Catherine", + "Cathey", + "Cathi", + "Cathie", + "Cathleen", + "Cathlene", + "Cathrin", + "Cathrine", + "Cathryn", + "Cathy", + "Cathyleen", + "Cati", + "Catie", + "Catima", + "Catina", + "Catlaina", + "Catlee", + "Catlin", + "Cato", + "Caton", + "Catrina", + "Catriona", + "Catt", + "Cattan", + "Cattier", + "Cattima", + "Catto", + "Catton", + "Caty", + "Caughey", + "Caundra", + "Cavallaro", + "Cavan", + "Cavanagh", + "Cavanaugh", + "Cave", + "Caves", + "Cavil", + "Cavill", + "Cavit", + "Cavuoto", + "Cawley", + "Caye", + "Cayla", + "Caylor", + "Cayser", + "Caz", + "Cazzie", + "Cchaddie", + "Cece", + "Cecelia", + "Cecil", + "Cecile", + "Ceciley", + "Cecilia", + "Cecilio", + "Cecilius", + "Cecilla", + "Cecily", + "Ced", + "Cedar", + "Cedell", + "Cedric", + "Ceevah", + "Ceil", + "Cele", + "Celene", + "Celeski", + "Celesta", + "Celeste", + "Celestia", + "Celestina", + "Celestine", + "Celestyn", + "Celestyna", + "Celia", + "Celie", + "Celik", + "Celin", + "Celina", + "Celinda", + "Celine", + "Celinka", + "Celio", + "Celisse", + "Celka", + "Celle", + "Cello", + "Celtic", + "Cenac", + "Cence", + "Centeno", + "Center", + "Centonze", + "Ceporah", + "Cerallua", + "Cerelia", + "Cerell", + "Cerellia", + "Cerelly", + "Cerf", + "Cerracchio", + "Certie", + "Cerveny", + "Cerys", + "Cesar", + "Cesare", + "Cesaria", + "Cesaro", + "Cestar", + "Cesya", + "Cha", + "Chabot", + "Chace", + "Chad", + "Chadabe", + "Chadbourne", + "Chadburn", + "Chadd", + "Chaddie", + "Chaddy", + "Chader", + "Chadwick", + "Chae", + "Chafee", + "Chaffee", + "Chaffin", + "Chaffinch", + "Chaiken", + "Chaille", + "Chaim", + "Chainey", + "Chaing", + "Chak", + "Chaker", + "Chally", + "Chalmer", + "Chalmers", + "Chamberlain", + "Chamberlin", + "Chambers", + "Chamkis", + "Champ", + "Champagne", + "Champaigne", + "Chan", + "Chance", + "Chancellor", + "Chancelor", + "Chancey", + "Chanda", + "Chandal", + "Chandler", + "Chandless", + "Chandos", + "Chandra", + "Chane", + "Chaney", + "Chang", + "Changaris", + "Channa", + "Channing", + "Chansoo", + "Chantal", + "Chantalle", + "Chao", + "Chap", + "Chapa", + "Chapel", + "Chapell", + "Chapen", + "Chapin", + "Chapland", + "Chapman", + "Chapnick", + "Chappelka", + "Chappell", + "Chappie", + "Chappy", + "Chara", + "Charbonneau", + "Charbonnier", + "Chard", + "Chari", + "Charie", + "Charil", + "Charin", + "Chariot", + "Charis", + "Charissa", + "Charisse", + "Charita", + "Charity", + "Charla", + "Charlean", + "Charleen", + "Charlena", + "Charlene", + "Charles", + "Charlet", + "Charleton", + "Charley", + "Charlie", + "Charline", + "Charlot", + "Charlotta", + "Charlotte", + "Charlton", + "Charmain", + "Charmaine", + "Charmane", + "Charmian", + "Charmine", + "Charmion", + "Charo", + "Charpentier", + "Charron", + "Charry", + "Charteris", + "Charters", + "Charyl", + "Chas", + "Chase", + "Chasse", + "Chassin", + "Chastain", + "Chastity", + "Chatav", + "Chatterjee", + "Chatwin", + "Chaudoin", + "Chaunce", + "Chauncey", + "Chavaree", + "Chaves", + "Chavey", + "Chavez", + "Chaworth", + "Che", + "Cheadle", + "Cheatham", + "Checani", + "Chee", + "Cheffetz", + "Cheke", + "Chellman", + "Chelsae", + "Chelsea", + "Chelsey", + "Chelsie", + "Chelsy", + "Chelton", + "Chem", + "Chema", + "Chemar", + "Chemaram", + "Chemarin", + "Chemash", + "Chemesh", + "Chemosh", + "Chemush", + "Chen", + "Chenay", + "Chenee", + "Cheney", + "Cheng", + "Cher", + "Chere", + "Cherey", + "Cheri", + "Cheria", + "Cherian", + "Cherianne", + "Cherice", + "Cherida", + "Cherie", + "Cherilyn", + "Cherilynn", + "Cherin", + "Cherise", + "Cherish", + "Cherlyn", + "Chernow", + "Cherri", + "Cherrita", + "Cherry", + "Chery", + "Cherye", + "Cheryl", + "Ches", + "Cheshire", + "Cheslie", + "Chesna", + "Chesney", + "Chesnut", + "Chessa", + "Chessy", + "Chester", + "Cheston", + "Chet", + "Cheung", + "Chev", + "Chevalier", + "Chevy", + "Chew", + "Cheyne", + "Cheyney", + "Chi", + "Chiaki", + "Chiang", + "Chiarra", + "Chic", + "Chick", + "Chickie", + "Chicky", + "Chico", + "Chicoine", + "Chien", + "Chil", + "Chilcote", + "Child", + "Childers", + "Childs", + "Chiles", + "Chill", + "Chilson", + "Chilt", + "Chilton", + "Chimene", + "Chin", + "China", + "Ching", + "Chinua", + "Chiou", + "Chip", + "Chipman", + "Chiquia", + "Chiquita", + "Chirlin", + "Chisholm", + "Chita", + "Chitkara", + "Chivers", + "Chladek", + "Chlo", + "Chloe", + "Chloette", + "Chloras", + "Chlores", + "Chlori", + "Chloris", + "Cho", + "Chobot", + "Chon", + "Chong", + "Choo", + "Choong", + "Chor", + "Chouest", + "Chow", + "Chretien", + "Chris", + "Chrisman", + "Chrisoula", + "Chrissa", + "Chrisse", + "Chrissie", + "Chrissy", + "Christa", + "Christabel", + "Christabella", + "Christabelle", + "Christal", + "Christalle", + "Christan", + "Christean", + "Christel", + "Christen", + "Christensen", + "Christenson", + "Christi", + "Christian", + "Christiana", + "Christiane", + "Christianity", + "Christianna", + "Christiano", + "Christiansen", + "Christianson", + "Christie", + "Christin", + "Christina", + "Christine", + "Christis", + "Christmann", + "Christmas", + "Christoffer", + "Christoforo", + "Christoper", + "Christoph", + "Christophe", + "Christopher", + "Christos", + "Christy", + "Christye", + "Christyna", + "Chrisy", + "Chrotoem", + "Chrysa", + "Chrysler", + "Chrystal", + "Chryste", + "Chrystel", + "Chu", + "Chuah", + "Chubb", + "Chuch", + "Chucho", + "Chuck", + "Chud", + "Chui", + "Chuipek", + "Chun", + "Chung", + "Chura", + "Church", + "Churchill", + "Chute", + "Chuu", + "Chyou", + "Cia", + "Cianca", + "Ciapas", + "Ciapha", + "Ciaphus", + "Cibis", + "Ciccia", + "Cicely", + "Cicenia", + "Cicero", + "Cichocki", + "Cicily", + "Cid", + "Cida", + "Ciel", + "Cila", + "Cilka", + "Cilla", + "Cilo", + "Cilurzo", + "Cima", + "Cimah", + "Cimbura", + "Cinda", + "Cindee", + "Cindelyn", + "Cinderella", + "Cindi", + "Cindie", + "Cindra", + "Cindy", + "Cinelli", + "Cini", + "Cinnamon", + "Cioban", + "Cioffred", + "Ciprian", + "Circosta", + "Ciri", + "Cirilla", + "Cirillo", + "Cirilo", + "Ciro", + "Cirone", + "Cirri", + "Cis", + "Cissie", + "Cissiee", + "Cissy", + "Cita", + "Citarella", + "Citron", + "Clabo", + "Claiborn", + "Claiborne", + "Clair", + "Claire", + "Claman", + "Clance", + "Clancy", + "Clapp", + "Clapper", + "Clara", + "Clarabelle", + "Clarance", + "Clardy", + "Clare", + "Clarence", + "Claresta", + "Clareta", + "Claretta", + "Clarette", + "Clarey", + "Clarhe", + "Clari", + "Claribel", + "Clarice", + "Clarie", + "Clarinda", + "Clarine", + "Clarisa", + "Clarise", + "Clarissa", + "Clarisse", + "Clarita", + "Clark", + "Clarke", + "Clarkin", + "Clarkson", + "Clary", + "Claud", + "Clauddetta", + "Claude", + "Claudell", + "Claudelle", + "Claudetta", + "Claudette", + "Claudia", + "Claudian", + "Claudianus", + "Claudie", + "Claudina", + "Claudine", + "Claudio", + "Claudius", + "Claudy", + "Claus", + "Clausen", + "Clava", + "Clawson", + "Clay", + "Clayberg", + "Clayborn", + "Clayborne", + "Claybourne", + "Clayson", + "Clayton", + "Clea", + "Cleary", + "Cleasta", + "Cleave", + "Cleaves", + "Cleavland", + "Clein", + "Cleland", + "Clellan", + "Clem", + "Clemen", + "Clemence", + "Clemens", + "Clement", + "Clementas", + "Clemente", + "Clementi", + "Clementia", + "Clementina", + "Clementine", + "Clementis", + "Clementius", + "Clements", + "Clemmie", + "Clemmy", + "Cleo", + "Cleodal", + "Cleodel", + "Cleodell", + "Cleon", + "Cleopatra", + "Cleopatre", + "Clerc", + "Clercq", + "Clere", + "Cleres", + "Clerissa", + "Clerk", + "Cleti", + "Cletis", + "Cletus", + "Cleve", + "Cleveland", + "Clevey", + "Clevie", + "Clie", + "Cliff", + "Cliffes", + "Clifford", + "Clift", + "Clifton", + "Clim", + "Cline", + "Clint", + "Clintock", + "Clinton", + "Clio", + "Clippard", + "Clite", + "Clive", + "Clo", + "Cloe", + "Cloots", + "Clorinda", + "Clorinde", + "Cloris", + "Close", + "Clothilde", + "Clotilda", + "Clotilde", + "Clough", + "Clougher", + "Cloutman", + "Clova", + "Clovah", + "Clover", + "Clovis", + "Clower", + "Clute", + "Cly", + "Clyde", + "Clymer", + "Clynes", + "Clyte", + "Clyve", + "Clywd", + "Cnut", + "Coad", + "Coady", + "Coates", + "Coats", + "Cob", + "Cobb", + "Cobbie", + "Cobby", + "Coben", + "Cochard", + "Cochran", + "Cochrane", + "Cock", + "Cockburn", + "Cocke", + "Cocks", + "Coco", + "Codd", + "Codding", + "Codee", + "Codel", + "Codi", + "Codie", + "Cody", + "Coe", + "Coffee", + "Coffeng", + "Coffey", + "Coffin", + "Cofsky", + "Cogan", + "Cogen", + "Cogswell", + "Coh", + "Cohbath", + "Cohberg", + "Cohbert", + "Cohby", + "Cohdwell", + "Cohe", + "Coheman", + "Cohen", + "Cohette", + "Cohin", + "Cohl", + "Cohla", + "Cohleen", + "Cohlette", + "Cohlier", + "Cohligan", + "Cohn", + "Cointon", + "Coit", + "Coke", + "Col", + "Colan", + "Colas", + "Colb", + "Colbert", + "Colburn", + "Colby", + "Colbye", + "Cole", + "Coleen", + "Coleman", + "Colene", + "Colet", + "Coletta", + "Colette", + "Coleville", + "Colfin", + "Colier", + "Colin", + "Colinson", + "Colis", + "Collar", + "Collayer", + "Collbaith", + "Colleen", + "Collen", + "Collete", + "Collette", + "Colley", + "Collie", + "Collier", + "Colligan", + "Collimore", + "Collin", + "Colline", + "Collins", + "Collis", + "Collum", + "Colly", + "Collyer", + "Colman", + "Colner", + "Colombi", + "Colon", + "Colp", + "Colpin", + "Colson", + "Colston", + "Colt", + "Coltin", + "Colton", + "Coltson", + "Coltun", + "Columba", + "Columbine", + "Columbus", + "Columbyne", + "Colver", + "Colvert", + "Colville", + "Colvin", + "Colwell", + "Colwen", + "Colwin", + "Colyer", + "Combe", + "Combes", + "Combs", + "Comfort", + "Compte", + "Comptom", + "Compton", + "Comras", + "Comstock", + "Comyns", + "Con", + "Conah", + "Conal", + "Conall", + "Conan", + "Conant", + "Conard", + "Concepcion", + "Concettina", + "Concha", + "Conchita", + "Concoff", + "Concordia", + "Condon", + "Coney", + "Congdon", + "Conger", + "Coniah", + "Conias", + "Conlan", + "Conlee", + "Conlen", + "Conley", + "Conlin", + "Conlon", + "Conn", + "Connel", + "Connell", + "Connelley", + "Connelly", + "Conner", + "Conners", + "Connett", + "Conney", + "Conni", + "Connie", + "Connolly", + "Connor", + "Connors", + "Conny", + "Conover", + "Conrad", + "Conrade", + "Conrado", + "Conroy", + "Consalve", + "Consolata", + "Constance", + "Constancia", + "Constancy", + "Constant", + "Constanta", + "Constantia", + "Constantin", + "Constantina", + "Constantine", + "Constantino", + "Consuela", + "Consuelo", + "Conte", + "Conti", + "Converse", + "Convery", + "Conway", + "Cony", + "Conyers", + "Cooe", + "Cook", + "Cooke", + "Cookie", + "Cooley", + "Coombs", + "Coonan", + "Coop", + "Cooper", + "Cooperman", + "Coopersmith", + "Cooperstein", + "Cope", + "Copeland", + "Copland", + "Coplin", + "Copp", + "Coppinger", + "Coppins", + "Coppock", + "Coppola", + "Cora", + "Corabel", + "Corabella", + "Corabelle", + "Coral", + "Coralie", + "Coraline", + "Coralyn", + "Coray", + "Corbet", + "Corbett", + "Corbie", + "Corbin", + "Corby", + "Cord", + "Cordalia", + "Cordeelia", + "Cordelia", + "Cordelie", + "Cordell", + "Corder", + "Cordey", + "Cordi", + "Cordie", + "Cordier", + "Cordle", + "Cordova", + "Cordula", + "Cordy", + "Coreen", + "Corel", + "Corell", + "Corella", + "Corena", + "Corenda", + "Corene", + "Coretta", + "Corette", + "Corey", + "Cori", + "Coridon", + "Corie", + "Corilla", + "Corin", + "Corina", + "Corine", + "Corinna", + "Corinne", + "Coriss", + "Corissa", + "Corkhill", + "Corley", + "Corliss", + "Corly", + "Cormac", + "Cormack", + "Cormick", + "Cormier", + "Cornall", + "Corneille", + "Cornel", + "Cornela", + "Cornelia", + "Cornelie", + "Cornelius", + "Cornell", + "Cornelle", + "Cornew", + "Corney", + "Cornia", + "Cornie", + "Cornish", + "Cornwall", + "Cornwell", + "Corny", + "Corotto", + "Correna", + "Correy", + "Corri", + "Corrianne", + "Corrie", + "Corrina", + "Corrine", + "Corrinne", + "Corron", + "Corry", + "Corsetti", + "Corsiglia", + "Corso", + "Corson", + "Cort", + "Cortie", + "Cortney", + "Corty", + "Corvese", + "Corvin", + "Corwin", + "Corwun", + "Cory", + "Coryden", + "Corydon", + "Cos", + "Cosenza", + "Cosetta", + "Cosette", + "Coshow", + "Cosimo", + "Cosma", + "Cosme", + "Cosmo", + "Cost", + "Costa", + "Costanza", + "Costanzia", + "Costello", + "Coster", + "Costin", + "Cote", + "Cotsen", + "Cott", + "Cotter", + "Cotterell", + "Cottle", + "Cottrell", + "Coucher", + "Couchman", + "Coughlin", + "Coulombe", + "Coulson", + "Coulter", + "Coumas", + "Countess", + "Courcy", + "Court", + "Courtenay", + "Courtland", + "Courtnay", + "Courtney", + "Courtund", + "Cousin", + "Cousins", + "Coussoule", + "Couture", + "Covell", + "Coveney", + "Cowan", + "Coward", + "Cowden", + "Cowen", + "Cower", + "Cowey", + "Cowie", + "Cowles", + "Cowley", + "Cown", + "Cox", + "Coy", + "Coyle", + "Cozmo", + "Cozza", + "Crabb", + "Craddock", + "Craggie", + "Craggy", + "Craig", + "Crain", + "Cralg", + "Cram", + "Cramer", + "Cran", + "Crandale", + "Crandall", + "Crandell", + "Crane", + "Craner", + "Cranford", + "Cranston", + "Crary", + "Craven", + "Craw", + "Crawford", + "Crawley", + "Creamer", + "Crean", + "Creath", + "Creedon", + "Creigh", + "Creight", + "Creighton", + "Crelin", + "Crellen", + "Crenshaw", + "Cresa", + "Crescantia", + "Crescen", + "Crescentia", + "Crescin", + "Crescint", + "Cresida", + "Crespi", + "Crespo", + "Cressi", + "Cressida", + "Cressler", + "Cressy", + "Crichton", + "Crifasi", + "Crim", + "Crin", + "Cris", + "Crisey", + "Crispa", + "Crispas", + "Crispen", + "Crispin", + "Crissie", + "Crissy", + "Crist", + "Crista", + "Cristabel", + "Cristal", + "Cristen", + "Cristi", + "Cristian", + "Cristiano", + "Cristie", + "Cristin", + "Cristina", + "Cristine", + "Cristiona", + "Cristionna", + "Cristobal", + "Cristoforo", + "Cristy", + "Criswell", + "Critchfield", + "Critta", + "Crocker", + "Crockett", + "Crofoot", + "Croft", + "Crofton", + "Croix", + "Crompton", + "Cromwell", + "Croner", + "Cronin", + "Crooks", + "Croom", + "Crosby", + "Crosley", + "Cross", + "Crosse", + "Croteau", + "Crotty", + "Crow", + "Crowe", + "Crowell", + "Crowley", + "Crowns", + "Croydon", + "Cruce", + "Crudden", + "Cruickshank", + "Crutcher", + "Cruz", + "Cryan", + "Crysta", + "Crystal", + "Crystie", + "Cthrine", + "Cuda", + "Cudlip", + "Culberson", + "Culbert", + "Culbertson", + "Culhert", + "Cull", + "Cullan", + "Cullen", + "Culley", + "Cullie", + "Cullin", + "Culliton", + "Cully", + "Culosio", + "Culver", + "Cumine", + "Cumings", + "Cummine", + "Cummings", + "Cummins", + "Cung", + "Cunningham", + "Cupo", + "Curcio", + "Curhan", + "Curkell", + "Curley", + "Curnin", + "Curr", + "Curran", + "Curren", + "Currey", + "Currie", + "Currier", + "Curry", + "Curson", + "Curt", + "Curtice", + "Curtis", + "Curzon", + "Cusack", + "Cusick", + "Custer", + "Cut", + "Cutcheon", + "Cutcliffe", + "Cuthbert", + "Cuthbertson", + "Cuthburt", + "Cutler", + "Cutlerr", + "Cutlip", + "Cutlor", + "Cutter", + "Cuttie", + "Cuttler", + "Cutty", + "Cuyler", + "Cy", + "Cyb", + "Cybil", + "Cybill", + "Cychosz", + "Cyd", + "Cykana", + "Cyler", + "Cyma", + "Cymbre", + "Cyn", + "Cyna", + "Cynar", + "Cynara", + "Cynarra", + "Cynde", + "Cyndi", + "Cyndia", + "Cyndie", + "Cyndy", + "Cynera", + "Cynth", + "Cynthea", + "Cynthia", + "Cynthie", + "Cynthla", + "Cynthy", + "Cyprian", + "Cyprio", + "Cypro", + "Cyprus", + "Cyrano", + "Cyrie", + "Cyril", + "Cyrill", + "Cyrilla", + "Cyrille", + "Cyrillus", + "Cyrus", + "Czarra", + "D'Arcy", + "Dabbs", + "Daberath", + "Dabney", + "Dace", + "Dacey", + "Dachi", + "Dachia", + "Dachy", + "Dacia", + "Dacie", + "Dacy", + "Daegal", + "Dael", + "Daffi", + "Daffie", + "Daffodil", + "Daffy", + "Dafna", + "Dafodil", + "Dag", + "Dagall", + "Daggett", + "Daggna", + "Dagley", + "Dagmar", + "Dagna", + "Dagnah", + "Dagney", + "Dagny", + "Dahl", + "Dahle", + "Dahlia", + "Dahlstrom", + "Daigle", + "Dail", + "Daile", + "Dailey", + "Daisey", + "Daisi", + "Daisie", + "Daisy", + "Daitzman", + "Dal", + "Dale", + "Dalenna", + "Daley", + "Dalia", + "Dalila", + "Dalis", + "Dall", + "Dallas", + "Dalli", + "Dallis", + "Dallman", + "Dallon", + "Daloris", + "Dalpe", + "Dalston", + "Dalt", + "Dalton", + "Dalury", + "Daly", + "Dam", + "Damal", + "Damalas", + "Damales", + "Damali", + "Damalis", + "Damalus", + "Damara", + "Damaris", + "Damarra", + "Dambro", + "Dame", + "Damek", + "Damian", + "Damiani", + "Damiano", + "Damick", + "Damicke", + "Damien", + "Damita", + "Damle", + "Damon", + "Damour", + "Dan", + "Dana", + "Danae", + "Danaher", + "Danais", + "Danas", + "Danby", + "Danczyk", + "Dane", + "Danell", + "Danella", + "Danelle", + "Danete", + "Danette", + "Daney", + "Danforth", + "Dang", + "Dani", + "Dania", + "Daniala", + "Danialah", + "Danica", + "Danice", + "Danie", + "Daniel", + "Daniela", + "Daniele", + "Daniell", + "Daniella", + "Danielle", + "Daniels", + "Danielson", + "Danieu", + "Danika", + "Danila", + "Danit", + "Danita", + "Daniyal", + "Dann", + "Danna", + "Dannel", + "Danni", + "Dannica", + "Dannie", + "Dannon", + "Danny", + "Dannye", + "Dante", + "Danuloff", + "Danya", + "Danyelle", + "Danyette", + "Danyluk", + "Danzig", + "Danziger", + "Dao", + "Daph", + "Daphene", + "Daphie", + "Daphna", + "Daphne", + "Dar", + "Dara", + "Darach", + "Darb", + "Darbee", + "Darbie", + "Darby", + "Darce", + "Darcee", + "Darcey", + "Darci", + "Darcia", + "Darcie", + "Darcy", + "Darda", + "Dardani", + "Dare", + "Dareece", + "Dareen", + "Darees", + "Darell", + "Darelle", + "Daren", + "Dari", + "Daria", + "Darian", + "Darice", + "Darill", + "Darin", + "Dario", + "Darius", + "Darken", + "Darla", + "Darleen", + "Darlene", + "Darline", + "Darlleen", + "Darmit", + "Darn", + "Darnall", + "Darnell", + "Daron", + "Darooge", + "Darra", + "Darrel", + "Darrell", + "Darrelle", + "Darren", + "Darrey", + "Darrick", + "Darrill", + "Darrin", + "Darrow", + "Darryl", + "Darryn", + "Darsey", + "Darsie", + "Dart", + "Darton", + "Darwen", + "Darwin", + "Darya", + "Daryl", + "Daryle", + "Daryn", + "Dash", + "Dasha", + "Dasi", + "Dasie", + "Dasteel", + "Dasya", + "Datha", + "Datnow", + "Daub", + "Daugherty", + "Daughtry", + "Daukas", + "Daune", + "Dav", + "Dave", + "Daveda", + "Daveen", + "Daven", + "Davena", + "Davenport", + "Daveta", + "Davey", + "David", + "Davida", + "Davidde", + "Davide", + "Davidoff", + "Davidson", + "Davie", + "Davies", + "Davilman", + "Davin", + "Davina", + "Davine", + "Davis", + "Davison", + "Davita", + "Davon", + "Davy", + "Dawes", + "Dawkins", + "Dawn", + "Dawna", + "Dawson", + "Day", + "Daye" +] \ No newline at end of file diff --git a/data/world/locations.json b/data/world/locations.json new file mode 100644 index 0000000..90ed8a3 --- /dev/null +++ b/data/world/locations.json @@ -0,0 +1,31 @@ +[{ + "latitude": 50.8577758, + "longitude": 5.6308645, + "name": "Maastricht", + "size": 2 + }, + { + "latitude": 48.8588377, + "longitude": 2.2770206, + "name": "Paris", + "size": 1 + }, + { + "latitude": 51.5285582, + "longitude": -0.2416796, + "name": "London", + "size": 1 + }, + { + "latitude": 55.8554403, + "longitude": -4.3024976, + "name": "Glasgow", + "size": 1 + }, + { + "latitude": 53.472225, + "longitude": -2.2935019, + "name": "Manchester", + "size": 1 + } +] \ No newline at end of file diff --git a/docs/Screenshots/loading_screen.png b/docs/Screenshots/loading_screen.png new file mode 100644 index 0000000..74e9ea4 Binary files /dev/null and b/docs/Screenshots/loading_screen.png differ diff --git a/docs/Screenshots/menu_screen.png b/docs/Screenshots/menu_screen.png new file mode 100644 index 0000000..57cd21f Binary files /dev/null and b/docs/Screenshots/menu_screen.png differ diff --git a/docs/Screenshots/menu_screen_loading.png b/docs/Screenshots/menu_screen_loading.png new file mode 100644 index 0000000..9311d4f Binary files /dev/null and b/docs/Screenshots/menu_screen_loading.png differ diff --git a/docs/Screenshots/save_state_select.png b/docs/Screenshots/save_state_select.png new file mode 100644 index 0000000..8095e6c Binary files /dev/null and b/docs/Screenshots/save_state_select.png differ diff --git a/docs/Screenshots/world_map.png b/docs/Screenshots/world_map.png new file mode 100644 index 0000000..648c458 Binary files /dev/null and b/docs/Screenshots/world_map.png differ diff --git a/docs/Screenshots/world_map_purchase_location.png b/docs/Screenshots/world_map_purchase_location.png new file mode 100644 index 0000000..590137e Binary files /dev/null and b/docs/Screenshots/world_map_purchase_location.png differ diff --git a/docs/Specification.docx b/docs/Specification.docx new file mode 100644 index 0000000..d8fbe74 Binary files /dev/null and b/docs/Specification.docx differ diff --git a/docs/Use Cases/use case diagram.png b/docs/Use Cases/use case diagram.png new file mode 100644 index 0000000..0d33ee4 Binary files /dev/null and b/docs/Use Cases/use case diagram.png differ diff --git a/docs/Use Cases/use case diagram.uxf b/docs/Use Cases/use case diagram.uxf new file mode 100644 index 0000000..8f27dad --- /dev/null +++ b/docs/Use Cases/use case diagram.uxf @@ -0,0 +1,711 @@ + + + 6 + + UMLActor + + 354 + 282 + 36 + 66 + + Player + + + + UMLUseCase + + 576 + 276 + 78 + 54 + + UC2: +Choose save state + + + + UMLGeneric + + 432 + 120 + 996 + 648 + + TruckerX +halign=left + + + + UMLUseCase + + 498 + 432 + 78 + 54 + + UC4: exit game + + + + UMLUseCase + + 570 + 354 + 78 + 54 + + UC3: manage +game settings + + + + UMLUseCase + + 714 + 276 + 78 + 54 + + UC5: select state to +continue + + + + UMLUseCase + + 552 + 198 + 78 + 54 + + UC1: start new game + + + + UMLUseCase + + 984 + 276 + 78 + 54 + + UC10: select place +to manage + + + + UMLUseCase + + 906 + 384 + 78 + 54 + + UC8: view events + + + + Relation + + 402 + 234 + 168 + 72 + + + 10.0;100.0;260.0;10.0 + + + Relation + + 402 + 294 + 186 + 24 + + + 10.0;20.0;290.0;20.0 + + + Relation + + 396 + 312 + 192 + 66 + + + 10.0;10.0;300.0;90.0 + + + Relation + + 390 + 324 + 150 + 120 + + + 10.0;10.0;230.0;180.0 + + + Relation + + 648 + 294 + 78 + 24 + + lt=<. +<<extends>> + 10.0;20.0;110.0;20.0 + + + Relation + + 924 + 294 + 72 + 24 + + lt=<. +<<extends>> + 10.0;20.0;100.0;20.0 + + + Relation + + 624 + 222 + 252 + 72 + + lt=.> +<<includes>> + 10.0;10.0;400.0;100.0 + + + Relation + + 888 + 324 + 78 + 72 + + lt=<. +<<extends>> + 10.0;10.0;80.0;100.0 + + + UMLUseCase + + 852 + 276 + 78 + 54 + + UC6: view world state + + + + Relation + + 786 + 294 + 78 + 24 + + lt=.> +<<includes>> + 10.0;20.0;110.0;20.0 + + + UMLUseCase + + 798 + 384 + 78 + 54 + + UC7: select place to +purchase + + + + Relation + + 840 + 324 + 72 + 72 + + lt=<. +<<extends>> + 60.0;10.0;10.0;100.0 + + + UMLUseCase + + 798 + 474 + 78 + 54 + + UC9: purchase place + + + + Relation + + 834 + 432 + 54 + 54 + + lt=<. +<<extends>> + 10.0;10.0;10.0;70.0 + + + UMLUseCase + + 1122 + 276 + 78 + 54 + + UC11: view place +state + + + + Relation + + 1056 + 294 + 78 + 24 + + lt=.> +<<includes>> + 10.0;20.0;110.0;20.0 + + + UMLUseCase + + 1002 + 384 + 78 + 54 + + UC12: +view employees + + + + UMLUseCase + + 1086 + 384 + 78 + 54 + + UC15: +view schedule + + + + UMLUseCase + + 1170 + 384 + 78 + 54 + + UC18: +view trucks + + + + UMLUseCase + + 1338 + 384 + 78 + 54 + + UC24: +view job offers + + + + Relation + + 1044 + 318 + 102 + 78 + + lt=<. +<<extends>> + 150.0;10.0;10.0;110.0 + + + Relation + + 1182 + 318 + 192 + 78 + + lt=<. +<<extends>> + 10.0;10.0;300.0;110.0 + + + Relation + + 1170 + 324 + 72 + 72 + + lt=<. +<<extends>> + 10.0;10.0;60.0;100.0 + + + Relation + + 1128 + 324 + 60 + 72 + + lt=<. +<<extends>> + 30.0;10.0;10.0;100.0 + + + UMLUseCase + + 1002 + 480 + 78 + 54 + + UC13: +select employee + + + + Relation + + 1038 + 432 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1002 + 576 + 78 + 54 + + UC14: +change assigned +truck + + + + Relation + + 1038 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + Relation + + 1116 + 432 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1086 + 480 + 78 + 54 + + UC16: +select job to +reschedule + + + + Relation + + 1116 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1086 + 576 + 78 + 54 + + UC17: +reschedule +timeslots for job + + + + Relation + + 1200 + 432 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1170 + 480 + 78 + 54 + + UC19: +select truck + + + + Relation + + 1200 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1170 + 576 + 78 + 54 + + UC20: +upgrade truck +parts + + + + Relation + + 1368 + 432 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1338 + 480 + 78 + 54 + + UC25: +select job offer + + + + Relation + + 1368 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1338 + 576 + 78 + 54 + + UC26: +schedule timeslots +for new job + + + + UMLUseCase + + 1254 + 480 + 78 + 54 + + UC21: +purchase +new truck + + + + Relation + + 1284 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 1254 + 576 + 78 + 54 + + UC22: +select manufacturer + + + + Relation + + 1230 + 426 + 72 + 66 + + lt=<. +<<extends>> + 10.0;10.0;100.0;90.0 + + + UMLUseCase + + 1254 + 672 + 78 + 54 + + UC23: +select truck +to purchase + + + + Relation + + 1284 + 624 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + Relation + + 954 + 426 + 72 + 66 + + lt=<. +<<extends>> + 100.0;10.0;10.0;90.0 + + + UMLUseCase + + 918 + 480 + 78 + 54 + + UC27: +hire new +employee + + + + Relation + + 954 + 528 + 54 + 60 + + lt=<. +<<extends>> + 10.0;10.0;10.0;80.0 + + + UMLUseCase + + 918 + 576 + 78 + 54 + + UC28: +select employee +to hire + + + diff --git a/docs/~$ecification.docx b/docs/~$ecification.docx new file mode 100644 index 0000000..34b5985 Binary files /dev/null and b/docs/~$ecification.docx differ diff --git a/libs/SDL2.lib b/libs/SDL2.lib new file mode 100644 index 0000000..ea6fa42 Binary files /dev/null and b/libs/SDL2.lib differ diff --git a/libs/SDL2_mixer.lib b/libs/SDL2_mixer.lib new file mode 100644 index 0000000..3b96d7d Binary files /dev/null and b/libs/SDL2_mixer.lib differ diff --git a/libs/libFLAC-8.dll b/libs/libFLAC-8.dll new file mode 100644 index 0000000..71f2e19 Binary files /dev/null and b/libs/libFLAC-8.dll differ diff --git a/libs/libmodplug-1.dll b/libs/libmodplug-1.dll new file mode 100644 index 0000000..7c05126 Binary files /dev/null and b/libs/libmodplug-1.dll differ diff --git a/libs/libmpg123-0.dll b/libs/libmpg123-0.dll new file mode 100644 index 0000000..c7809b1 Binary files /dev/null and b/libs/libmpg123-0.dll differ diff --git a/libs/libogg-0.dll b/libs/libogg-0.dll new file mode 100644 index 0000000..5133481 Binary files /dev/null and b/libs/libogg-0.dll differ diff --git a/libs/libopus-0.dll b/libs/libopus-0.dll new file mode 100644 index 0000000..9ba6c38 Binary files /dev/null and b/libs/libopus-0.dll differ diff --git a/libs/libopusfile-0.dll b/libs/libopusfile-0.dll new file mode 100644 index 0000000..97a88b6 Binary files /dev/null and b/libs/libopusfile-0.dll differ diff --git a/libs/libvorbis-0.dll b/libs/libvorbis-0.dll new file mode 100644 index 0000000..f5ae1bf Binary files /dev/null and b/libs/libvorbis-0.dll differ diff --git a/libs/libvorbisfile-3.dll b/libs/libvorbisfile-3.dll new file mode 100644 index 0000000..d078736 Binary files /dev/null and b/libs/libvorbisfile-3.dll differ diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..3698908 --- /dev/null +++ b/src/data.c @@ -0,0 +1,113 @@ +void data_load() +{ + // Loading screen + img_logo = assets_load_image_from_file("data/img/logo.png"); + + // Fonts + for (int i = 0; i < FONT_COUNT; i++) { + u16 size = FONT_START + (FONT_SIZE_SPACING * i); + font_regular[i] = assets_load_font_from_file("data/fonts/Exo-Regular.ttf", size); + } + + // Images + img_logo_fruitosis = assets_load_image_from_file("data/img/logo_fruitosis.png"); + + img_truck_unknown = assets_load_image_from_file("data/img/truck-unknown.png"); + img_iveco_stralis_hiway = assets_load_image_from_file("data/img/iveco-stralis-hiway.png"); + img_iveco_stralis_activespace = assets_load_image_from_file("data/img/iveco-stralis-activespace.png"); + + img_logo_mercedes = assets_load_image_from_file("data/img/mercedes-logo.png"); + img_logo_iveco = assets_load_image_from_file("data/img/iveco-logo.png"); + img_logo_volvo = assets_load_image_from_file("data/img/volvo-logo.png"); + + img_white = assets_load_image_from_file("data/img/white.png"); + img_location_pin = assets_load_image_from_file("data/img/location-pin.png"); + img_city = assets_load_image_from_file("data/img/city.png"); + img_boat = assets_load_image_from_file("data/img/boat.png"); + img_star = assets_load_image_from_file("data/img/star.png"); + img_road = assets_load_image_from_file("data/img/road.png"); + img_timer = assets_load_image_from_file("data/img/timer.png"); + img_coins = assets_load_image_from_file("data/img/coins.png"); + img_arrow_left_rounded = assets_load_image_from_file("data/img/arrow-left-rounded.png"); + img_globe = assets_load_image_from_file("data/img/globe.png"); + img_questionmark = assets_load_image_from_file("data/img/question-mark.png"); + img_checkmark = assets_load_image_from_file("data/img/checkmark.png"); + img_grid = assets_load_image_from_file("data/img/grid.png"); + img_lock = assets_load_image_from_file("data/img/lock.png"); + img_arrow_left = assets_load_image_from_file("data/img/arrow-left.png"); + img_arrow_right = assets_load_image_from_file("data/img/arrow-right.png"); + img_pause = assets_load_image_from_file("data/img/pause.png"); + img_close = assets_load_image_from_file("data/img/close.png"); + img_back = assets_load_image_from_file("data/img/back.png"); + img_locationdot = assets_load_image_from_file("data/img/location_dot.png"); + img_dot = assets_load_image_from_file("data/img/dot.png"); + img_carwheel = assets_load_image_from_file("data/img/car-wheel.png"); + img_bank = assets_load_image_from_file("data/img/bank.png"); + img_graph = assets_load_image_from_file("data/img/statistics.png"); + img_list = assets_load_image_from_file("data/img/list.png"); + img_tabitem = assets_load_image_from_file("data/img/tab-item.png"); + img_portrait = assets_load_image_from_file("data/img/portrait.png"); + img_resume = assets_load_image_from_file("data/img/resume.png"); + img_signature = assets_load_image_from_file("data/img/signature.png"); + img_hired = assets_load_image_from_file("data/img/hired.png"); + img_denied = assets_load_image_from_file("data/img/denied.png"); + img_world_map = assets_load_image_from_file("data/img/world_background.png"); + + img_panel_bottom = assets_load_image_from_file("data/img/panel_bottom.png"); + img_panel_top = assets_load_image_from_file("data/img/panel_top.png"); + img_panel_left = assets_load_image_from_file("data/img/panel_left.png"); + img_panel_right = assets_load_image_from_file("data/img/panel_right.png"); + img_panel_bottomleft = assets_load_image_from_file("data/img/panel_bottomleft.png"); + img_panel_bottomright = assets_load_image_from_file("data/img/panel_bottomright.png"); + img_panel_topleft = assets_load_image_from_file("data/img/panel_topleft.png"); + img_panel_topright = assets_load_image_from_file("data/img/panel_topright.png"); + + img_button_bottom = assets_load_image_from_file("data/img/button_bottom.png"); + img_button_top = assets_load_image_from_file("data/img/button_top.png"); + img_button_left = assets_load_image_from_file("data/img/button_left.png"); + img_button_right = assets_load_image_from_file("data/img/button_right.png"); + img_button_bottomleft = assets_load_image_from_file("data/img/button_bottomleft.png"); + img_button_bottomright = assets_load_image_from_file("data/img/button_bottomright.png"); + img_button_topleft = assets_load_image_from_file("data/img/button_topleft.png"); + img_button_topright = assets_load_image_from_file("data/img/button_topright.png"); + + img_portrait_body = assets_load_image_from_file("data/img/portrait/body.png"); + img_portrait_head = assets_load_image_from_file("data/img/portrait/face.png"); + for (s32 i = 0; i < PORTRAIT_MAX_HAIR_COUNT; i++) + { + char hair_path[50]; + sprintf(hair_path, "data/img/portrait/hair/hair%d.png", i+1); + img_portrait_hair[i] = assets_load_image_from_file(hair_path); + + } + + // Sound effects + snd_click = assets_load_wav_from_file("data/sounds/click.wav"); + snd_click2 = assets_load_wav_from_file("data/sounds/click2.wav"); + snd_click3 = assets_load_wav_from_file("data/sounds/click3.wav"); + snd_event = assets_load_wav_from_file("data/sounds/event.wav"); + snd_accelerate = assets_load_wav_from_file("data/sounds/accelerate.wav"); + + // Songs + { + platform_set_active_directory(binary_path); + + array files = array_create(sizeof(found_file)); + array filters = string_split("*.mp3"); + bool is_cancelled = false; + platform_list_files_block(&files, "data/music/", filters, true, 0, true, &is_cancelled, 0); + log_assert(files.length < SOUNGS_COUNT, "Not enough space for songs."); + for (s32 i = 0; i < files.length; i++) + { + found_file *file = array_at(&files, i); + + if (platform_file_exists(file->path)) + { + snd_songs[i] = assets_load_music_from_file(file->path); + } + } + + array_destroy(&files); + array_destroy(&filters); + } +} \ No newline at end of file diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..de41182 --- /dev/null +++ b/src/game.c @@ -0,0 +1,134 @@ + +static game _game_instance; + +static void game_init_current_scene() +{ + switch (_game_instance.current_state) + { + case GAME_STATE_LOADING: + loading_scene_init(); + break; + case GAME_STATE_MENU: + menu_scene_init(); + break; + case GAME_STATE_SELECT_SAVE: + save_state_select_scene_init(); + break; + case GAME_STATE_WORLD_MAP: + world_map_scene_init(); + break; + case GAME_STATE_LOADING_WORLD: + loading_world_scene_init(); + break; + case GAME_STATE_ERROR: + error_scene_init(); + break; + case GAME_STATE_PLACE_DETAIL: + place_detail_scene_init(); + break; + case GAME_STATE_SETTINGS: + settings_scene_init(); + break; + } +} + +static void game_destroy_current_scene() +{ + switch (_game_instance.current_state) + { + case GAME_STATE_LOADING: + loading_scene_destroy(); + break; + case GAME_STATE_MENU: + menu_scene_destroy(); + break; + case GAME_STATE_SELECT_SAVE: + save_state_select_scene_destroy(); + break; + case GAME_STATE_WORLD_MAP: + world_map_scene_destroy(); + break; + case GAME_STATE_LOADING_WORLD: + loading_world_scene_destroy(); + break; + case GAME_STATE_ERROR: + error_scene_destroy(); + break; + case GAME_STATE_PLACE_DETAIL: + place_detail_scene_destroy(); + break; + case GAME_STATE_SETTINGS: + settings_scene_destroy(); + break; + } +} + +void game_set_active_scene(game_state state) +{ + game_destroy_current_scene(); + _game_instance.current_state = state; + game_init_current_scene(); +} + +void game_update(platform_window* window) +{ + switch (_game_instance.current_state) + { + case GAME_STATE_LOADING: + loading_scene_update(window); + break; + case GAME_STATE_MENU: + menu_scene_update(window); + break; + case GAME_STATE_SELECT_SAVE: + save_state_select_scene_update(window); + break; + case GAME_STATE_WORLD_MAP: + world_map_scene_update(window); + break; + case GAME_STATE_LOADING_WORLD: + loading_world_scene_update(window); + break; + case GAME_STATE_ERROR: + error_scene_update(window); + break; + case GAME_STATE_PLACE_DETAIL: + place_detail_scene_update(window); + break; + case GAME_STATE_SETTINGS: + settings_scene_update(window); + break; + } +} + +void game_render(platform_window* window) +{ + switch (_game_instance.current_state) + { + case GAME_STATE_LOADING: + loading_scene_render(window); + break; + case GAME_STATE_MENU: + menu_scene_render(window); + break; + case GAME_STATE_SELECT_SAVE: + save_state_select_scene_render(window); + break; + case GAME_STATE_WORLD_MAP: + world_map_scene_render(window); + break; + case GAME_STATE_LOADING_WORLD: + loading_world_scene_render(window); + break; + case GAME_STATE_ERROR: + error_scene_render(window); + break; + case GAME_STATE_PLACE_DETAIL: + place_detail_scene_render(window); + break; + case GAME_STATE_SETTINGS: + settings_scene_render(window); + break; + } + update_render_tooltip(); +} \ No newline at end of file diff --git a/src/include/data.h b/src/include/data.h new file mode 100644 index 0000000..1f13791 --- /dev/null +++ b/src/include/data.h @@ -0,0 +1,100 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_DATA +#define INCLUDE_DATA + +#define FONT_SIZE_SPACING 4 +#define FONT_COUNT 20 +#define FONT_START 8 + +image* img_logo_fruitosis; + +image* img_truck_unknown; +image* img_iveco_stralis_activespace; +image* img_iveco_stralis_hiway; + +image* img_logo_mercedes; +image* img_logo_iveco; +image* img_logo_volvo; + +image* img_white; +image* img_location_pin; +image* img_city; +image* img_boat; +image* img_star; +image* img_road; +image* img_timer; +image* img_coins; +image* img_arrow_left_rounded; +image* img_globe; +image* img_questionmark; +image* img_checkmark; +image* img_grid; +image* img_lock; +image* img_arrow_left; +image* img_arrow_right; +image* img_logo; +image* img_pause; +image* img_close; +image* img_back; +image* img_locationdot; +image* img_dot; +image* img_carwheel; +image* img_graph; +image* img_list; +image* img_bank; +image* img_tabitem; +image* img_portrait; +image* img_resume; +image* img_signature; +image* img_hired; +image* img_denied; +image* img_world_map; + +image* img_button_bottom; +image* img_button_top; +image* img_button_left; +image* img_button_right; +image* img_button_bottomleft; +image* img_button_bottomright; +image* img_button_topleft; +image* img_button_topright; + +image* img_panel_bottom; +image* img_panel_top; +image* img_panel_left; +image* img_panel_right; +image* img_panel_bottomleft; +image* img_panel_bottomright; +image* img_panel_topleft; +image* img_panel_topright; + +#define PORTRAIT_MAX_HAIR_COUNT 5 +image* img_portrait_body; +image* img_portrait_head; +image* img_portrait_hair[PORTRAIT_MAX_HAIR_COUNT]; + +font* font_regular[FONT_COUNT]; + +sound* snd_click; +sound* snd_click2; +sound* snd_click3; +sound* snd_event; +sound* snd_accelerate; + +#define SOUNGS_COUNT 30 +sound* snd_songs[SOUNGS_COUNT]; + +void data_load(); + +font empty_font_d = {0}; + +#define SIZE_RDF(_w, _size) ((s32)(_size * (_w) + 3) & ~0x03) +#define SIZE_RD(_w, _size) ((s32)((_size * (_w/1280.0f)) + 3) & ~0x03) +#define FONT_REGULAR(_size) (_size < FONT_START || _size > (FONT_START+(FONT_SIZE_SPACING*FONT_COUNT))) ? (&empty_font_d) : font_regular[(_size-FONT_START)/FONT_SIZE_SPACING]; + +#endif \ No newline at end of file diff --git a/src/include/game.h b/src/include/game.h new file mode 100644 index 0000000..2c09ada --- /dev/null +++ b/src/include/game.h @@ -0,0 +1,31 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_GAME +#define INCLUDE_GAME + +typedef enum t_game_state +{ + GAME_STATE_LOADING = 1, + GAME_STATE_MENU = 2, + GAME_STATE_SELECT_SAVE = 3, + GAME_STATE_WORLD_MAP = 4, + GAME_STATE_LOADING_WORLD = 5, + GAME_STATE_ERROR = 6, + GAME_STATE_PLACE_DETAIL = 7, + GAME_STATE_SETTINGS = 8, +} game_state; + +typedef struct t_game +{ + game_state current_state; +} game; + +void game_set_active_scene(game_state state); +void game_update(platform_window* window); +void game_render(platform_window* window); + +#endif \ No newline at end of file diff --git a/src/include/scenery.h b/src/include/scenery.h new file mode 100644 index 0000000..08afec0 --- /dev/null +++ b/src/include/scenery.h @@ -0,0 +1,29 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_SCENERY +#define INCLUDE_SCENERY + +#define BOAT_ROUTE_MAX_POINTS 300 + +typedef struct t_boat_route_point +{ + float x; + float y; +} boat_route_point; + +typedef struct t_boat_route +{ + boat_route_point points[BOAT_ROUTE_MAX_POINTS]; + u8 count; + u8 current_point; + float current_point_duration; + bool reversed; +} boat_route; + +void update_render_scenery(world* world); + +#endif \ No newline at end of file diff --git a/src/include/scenes/error_scene.h b/src/include/scenes/error_scene.h new file mode 100644 index 0000000..b2b9a0d --- /dev/null +++ b/src/include/scenes/error_scene.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_ERROR_SCENE +#define INCLUDE_ERROR_SCENE + +void error_scene_init(); +void error_scene_render(platform_window* window); +void error_scene_update(platform_window* window); +void error_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/loading_scene.h b/src/include/scenes/loading_scene.h new file mode 100644 index 0000000..3e49a9a --- /dev/null +++ b/src/include/scenes/loading_scene.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_LOADING_SCENE +#define INCLUDE_LOADING_SCENE + +void loading_scene_init(); +void loading_scene_render(platform_window* window); +void loading_scene_update(platform_window* window); +void loading_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/loading_world_scene.h b/src/include/scenes/loading_world_scene.h new file mode 100644 index 0000000..864fe8d --- /dev/null +++ b/src/include/scenes/loading_world_scene.h @@ -0,0 +1,16 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_LOADING_WORLD_SCENE +#define INCLUDE_LOADING_WORLD_SCENE + +void loading_world_scene_init(); +void start_loading_world(char* saved_file_path); +void loading_world_scene_render(platform_window* window); +void loading_world_scene_update(platform_window* window); +void loading_world_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/menu_scene.h b/src/include/scenes/menu_scene.h new file mode 100644 index 0000000..e68b778 --- /dev/null +++ b/src/include/scenes/menu_scene.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_MENU_SCENE +#define INCLUDE_MENU_SCENE + +void menu_scene_init(); +void menu_scene_render(platform_window* window); +void menu_scene_update(platform_window* window); +void menu_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/place_detail.h b/src/include/scenes/place_detail.h new file mode 100644 index 0000000..91c34ab --- /dev/null +++ b/src/include/scenes/place_detail.h @@ -0,0 +1,16 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_PLACE_DETAIL_SCENE +#define INCLUDE_PLACE_DETAIL_SCENE + +void place_detail_scene_init(); +void place_detail_set_active_location(world_location* location); +void place_detail_scene_render(platform_window* window); +void place_detail_scene_update(platform_window* window); +void place_detail_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/save_state_select.h b/src/include/scenes/save_state_select.h new file mode 100644 index 0000000..260bec2 --- /dev/null +++ b/src/include/scenes/save_state_select.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_SELECT_SAVE_SCENE +#define INCLUDE_SELECT_SAVE_SCENE + +void save_state_select_scene_init(); +void save_state_select_scene_render(platform_window* window); +void save_state_select_scene_update(platform_window* window); +void save_state_select_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/settings_scene.h b/src/include/scenes/settings_scene.h new file mode 100644 index 0000000..701e6a9 --- /dev/null +++ b/src/include/scenes/settings_scene.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_SETTINGS_SCENE +#define INCLUDE_SETTINGS_SCENE + +void settings_scene_init(); +void settings_scene_render(platform_window* window); +void settings_scene_update(platform_window* window); +void settings_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/scenes/world_map.h b/src/include/scenes/world_map.h new file mode 100644 index 0000000..d678868 --- /dev/null +++ b/src/include/scenes/world_map.h @@ -0,0 +1,16 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_WORLD_MAP_SCENE +#define INCLUDE_WORLD_MAP_SCENE + +void world_map_set_active_world(world* world); +void world_map_scene_init(); +void world_map_scene_render(platform_window* window); +void world_map_scene_update(platform_window* window); +void world_map_scene_destroy(); + +#endif \ No newline at end of file diff --git a/src/include/settings.h b/src/include/settings.h new file mode 100644 index 0000000..cba99ad --- /dev/null +++ b/src/include/settings.h @@ -0,0 +1,28 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_SETTINGS +#define INCLUDE_SETTINGS + +#define AUDIO_CHANNEL_SFX_1 1 +#define AUDIO_CHANNEL_SFX_2 2 +#define AUDIO_CHANNEL_SFX_3 3 + +u16 key_back = KEY_ESCAPE; +u16 key_accept = KEY_ENTER; +u16 key_insights_graph = KEY_F1; +u16 key_insights_chart = KEY_F2; +u16 key_events = KEY_F3; +u16 key_bank = KEY_F4; + +float volume_global = 0.2f; +float volume_music = 0.2f; +float volume_sfx = 0.2f; + +bool option_vsync = true; +bool option_fullscreen = false; + +#endif \ No newline at end of file diff --git a/src/include/tooltip.h b/src/include/tooltip.h new file mode 100644 index 0000000..8748c81 --- /dev/null +++ b/src/include/tooltip.h @@ -0,0 +1,18 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_TOOPTIP +#define INCLUDE_TOOPTIP + +char tooltip_buffer[100]; +s32 tooltip_x = 0; +s32 tooltip_y = 0; +bool tooltop_visible = false; + +void show_tooltip(s32 x, s32 y, char* buf); +void update_render_tooltip(); + +#endif \ No newline at end of file diff --git a/src/include/ui/animation.h b/src/include/ui/animation.h new file mode 100644 index 0000000..1a10c0f --- /dev/null +++ b/src/include/ui/animation.h @@ -0,0 +1,24 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_ANIMATION +#define INCLUDE_ANIMATION + +typedef struct t_animation +{ + float time; + bool started; + float duration; + float percentage; +} animation; + +#define AN_LI(_cx,_dx,_an) (_cx + (_dx-_cx)*_an.percentage) +#define AN_LI_TINT(_color, _an) rgba((_color).r,(_color).g,(_color).b,255*_an.percentage) + +animation animation_create(s32 duration); +float animation_update(animation* an); + +#endif \ No newline at end of file diff --git a/src/include/ui/button.h b/src/include/ui/button.h new file mode 100644 index 0000000..e381631 --- /dev/null +++ b/src/include/ui/button.h @@ -0,0 +1,20 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_BUTTON +#define INCLUDE_BUTTON + +typedef enum t_button_type +{ + BUTTON_STATIC = 0, + BUTTON_ENABLED = 1, + BUTTON_DISABLED = 2, + BUTTON_HIGHLIGHTED = 3, +} button_type; + +bool button_render(float scale, button_type enabled, char* text, s32 x, s32 y, s32 w, s32 h); + +#endif \ No newline at end of file diff --git a/src/include/ui/colors.h b/src/include/ui/colors.h new file mode 100644 index 0000000..f8a7f91 --- /dev/null +++ b/src/include/ui/colors.h @@ -0,0 +1,62 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_COLORS +#define INCLUDE_COLORS + +#define COLOR_WORLD_MAP_BACKGROUND rgb(32,52,63) +#define COLOR_WHITE rgb(255,255,255) +#define COLOR_BLACK rgb(0,0,0) +#define COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION rgb(40,40,40) +#define COLOR_DOT_HOVERED rgb(100,220,100) + +#define COLOR_TEXT_NEGATIVE rgb(226,86,86) +#define COLOR_TITLE rgb(8, 10, 12) +#define COLOR_TEXT_SHADOW rgb(24,24,24) +#define COLOR_TEXT rgb(207,207,207) + +#define COLOR_BUTTON_DISABLED_TINT rgb(70,70,70) + +#define COLOR_BUTTON_HIGHLIGHTED_TINT rgb(240,160,160) +#define COLOR_BUTTON_ACTIVE_TINT rgb(210,210,210) +#define COLOR_PANEL_BACKGROUND rgb(66,63,58) + +#define COLOR_BUTTON rgb(98, 95, 90) +#define COLOR_BUTTON_ACTIVE rgb(81, 78, 74) +#define COLOR_BUTTON_HIGHLIGHTED_ACTIVE rgb(92, 60, 56) +#define COLOR_BUTTON_DISABLED rgb(27, 26, 25) + +#define COLOR_LOCATION_DOT_UNOWNED rgb(220,220,220) +#define COLOR_LOCATION_DOT_OWNED rgb(220,100,100) + +#define COLOR_LIST_ENTRY_BACKGROUND rgb(46,43,40) +#define COLOR_LIST_ENTRY_BACKGROUND_ACTIVE rgb(63,59,56) + +#define COLOR_SCHEDULE_ROW_ACTIVE rgb(118,115,100) +#define COLOR_SCHEDULE_TILE_FIXED rgb(178,239,155) +#define COLOR_SCHEDULE_TILE_HOVERED rgb(26,200,237) +#define COLOR_SCHEDULE_TILE_INVALID rgb(244,70,73) +#define COLOR_SCHEDULE_TILE_INVALID_SELECTED rgb(252,159,160) +#define COLOR_SCHEDULE_TILE_SELECTED rgb(174,212,230) +#define COLOR_SCHEDULE_TILE_HIGHLIGHTED rgb(237,177,26) + +#define COLOR_SELECTOR_UNDERLINE rgb(180,180,180) + +#define LEGENDA_HOVER_BACKGROUND_COLOR rgba(255,0,0,50) +#define LEGENDA_COLOR_DISABLED rgb(120,120,120) +#define LEGENDA_SUB_COLOR_DISABLED rgba(120,120,120,100) + +#define COLOR_SCHEDULE_BG rgb(142,138,132) +#define COLOR_SCHEDULE_BORDER rgb(75,73,69) +#define COLOR_SCHEDULE_BORDER_THIN rgb(88,85,80) + +#define COLOR_TEXTBOX_TINT rgb(100,100,100) +#define COLOR_TEXTBOX_FILL rgb(38, 37, 35) + +#define COLOR_WRONG rgb(168,45,45) +#define COLOR_CORRECT rgb(47,168,45) + +#endif \ No newline at end of file diff --git a/src/include/ui/panel.h b/src/include/ui/panel.h new file mode 100644 index 0000000..83103d2 --- /dev/null +++ b/src/include/ui/panel.h @@ -0,0 +1,15 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_PANEL +#define INCLUDE_PANEL + +// 1280 is our reference width. +#define UI_SCALE(_w) (_w/1280.0f) + +void panel_render(float scale, s32 x, s32 y, s32 w, s32 h); + +#endif \ No newline at end of file diff --git a/src/include/ui/portrait.h b/src/include/ui/portrait.h new file mode 100644 index 0000000..8bff8e1 --- /dev/null +++ b/src/include/ui/portrait.h @@ -0,0 +1,28 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_PORTRAIT +#define INCLUDE_PORTRAIT + +color hair_palette[] = { + rgb(199, 186, 168), rgb(188, 181, 160), rgb(191, 173, 148), rgb(203, 181, 138), rgb(187, 162, 120), + rgb(174, 153, 122), rgb(172, 134, 109), rgb(205, 169, 129), rgb(168, 115, 88), rgb(137, 82, 71), + rgb(158, 131, 99), rgb(150, 116, 93), rgb(128, 93, 73), rgb(202, 162, 136), rgb(197, 146, 137), + rgb(194, 136, 129), rgb(165, 127, 117), rgb(152, 111, 113), rgb(123, 103, 93), rgb(98, 79, 73) +}; + +color skin_palette[] = { + rgb(233, 203, 167), rgb(238, 208, 183), rgb(247, 221, 196), rgb(247, 226, 171), rgb(239, 199, 148), rgb(239, 192, 136), + rgb(231, 188, 145), rgb(236, 192, 131), rgb(208, 158, 125), rgb(203, 150, 98), rgb(171, 139, 100), rgb(148, 98, 61), +}; + +color body_palette[] = { + rgb(33, 28, 32), rgb(76, 74, 77), rgb(109, 103, 107), rgb(171, 133, 86), rgb(213, 175, 126), rgb(240, 229, 225), +}; + +void draw_employee_portrait(employee* emp, float x, float y, float w, float h); + +#endif \ No newline at end of file diff --git a/src/include/ui/selectors.h b/src/include/ui/selectors.h new file mode 100644 index 0000000..6832ff7 --- /dev/null +++ b/src/include/ui/selectors.h @@ -0,0 +1,13 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_EMPLOYEE_SELECTOR +#define INCLUDE_EMPLOYEE_SELECTOR + +employee* employee_selector_render(platform_window* window, float scale, bool enabled, employee* current_val, s32 x, s32 y, s32 w, s32 h, animation an, scheduled_job* offer); +world_location* location_selector_render(platform_window* window, float scale, bool enabled, world_location* current_val, s32 x, s32 y, s32 w, s32 h); + +#endif \ No newline at end of file diff --git a/src/include/world.h b/src/include/world.h new file mode 100644 index 0000000..4f8af10 --- /dev/null +++ b/src/include/world.h @@ -0,0 +1,383 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_WORLD +#define INCLUDE_WORLD + +#define MAX_WORLD_LOCATION_NAME_LENGTH 20 +#define MAX_ENPOLYEE_FIRSTNAME_LENGTH 14 +#define MAX_ENPOLYEE_LASTNAME_LENGTH 14 +#define MAX_EMPLOYEE_NAME_LENGTH 30 +#define MAX_COMPANY_NAME_LENGTH 50 +#define MAX_DEALER_NAME_LENGTH 20 +#define MAX_TRUCK_NAME_LENGTH 30 +#define MAX_PRODUCT_NAME_LENGTH 50 +#define MAX_EMPLOYEE_NR_LENGTH 12 +#define MAX_INPUT_LENGTH_FOR_EMPLOYEE_SELECTOR 10 + +// Static = loaded from file +// Save State = loaded from save state +// Dynamic = set at runtime + +#define MINUTES(_n) (_n*60) +#define HOURS(_n) (_n*MINUTES(60)) +#define DAYS(_n) (_n*HOURS(24)) + +typedef struct t_world_location world_location; +typedef struct t_employee employee; + +#define WORK_HOUR_START 8 +#define WORK_HOUR_END 20 +#define TIME_SLOTS_PER_HOUR 4 +#define TIME_SLOTS_PER_DAY ((WORK_HOUR_END-WORK_HOUR_START)*TIME_SLOTS_PER_HOUR) +#define TIME_SLOTS_PER_WEEK (TIME_SLOTS_PER_DAY*7) + +#define MAX_JOBOFFER_COUNT 25 +#define MAX_EMPLOYEE_COUNT (TIME_SLOTS_PER_WEEK) +#define MAX_TRUCK_COUNT (TIME_SLOTS_PER_WEEK) + +#define MISSED_DELIVERY_TRUST_PENALTY (0.5f) +#define MAX_EFFECTIVE_EXPERIENCE 25.0f // anything past 25 years of experience has no extra positives. +#define SHIPTIME_DURATION_MULTIPLIER_MAX 1.0f +#define SHIPTIME_DURATION_MULTIPLIER_MIN 1.15f +#define DIESEL_PRICE_PER_LITER 1.4f + +#define INVALID_ID 0 +#define MAX_WORKED_HOURS_WEEKLY (45.0f) +#define MINIMUM_EMPLOYEE_HAPPINESS (0.4f) +#define EMPLOYEE_MAX_UNHAPPY_DAYS_BEFORE_QUITTING (60.0f) + +#define CDAYTORDAY(_day) (_day == 0 ? 7 : _day) // m = 1, s = 7 +#define RDAYTOCDAY(_day) (_day == 7 ? 0 : _day) // m = 1, s = 0 + +#define BASE_PAY 1800 +#define RAISE_PER_YEAR 55 +#define MAX_PAY 3250 + +typedef enum t_minor_event +{ + EXTERNAL_INSPECTION, // Check overworking, safety +} minor_event; + +typedef enum t_weekday +{ + MONDAY = 1, + TUESDAY = 2, + WEDNESDAY = 3, + THURSDAY = 4, + FRIDAY = 5, + SATURDAY = 6, + SUNDAY = 0, + + DAY_INVALID = 99 +} weekday; + +typedef struct t_product +{ + char name[MAX_PRODUCT_NAME_LENGTH]; +} product; + +typedef struct t_company +{ + // Static + char name[MAX_COMPANY_NAME_LENGTH]; + array products; + + // Dynamic + image* logo; +} company; + +typedef struct t_truck +{ + // Static + char name[MAX_TRUCK_NAME_LENGTH]; + u16 hp; + u32 price; + u16 fuelcapacity; + u16 torque; + float fuelusage; + + // Dynamic + image* logo; + + // Save State + s32 type; + s32 id; + employee* assigned_employee; +} truck; + +typedef struct t_truck_dealer +{ + // Static + char name[MAX_DEALER_NAME_LENGTH]; + array trucks; + + // Dynamic + image* logo; +} truck_dealer; + +#define JOB_OFFER_REWARD_PER_CONNECTION 160 +#define MAX_SHIPDAYS 4 + +typedef struct t_job_offer +{ + u32 id; + s64 expire_date; + company* company; + product* product; + s32 shipday_count; + weekday shipdays[MAX_SHIPDAYS]; + u32 reward; + array connections; // Should not be freed if offer has been accepted. + double total_distance; // in KM + time_t duration_sec_min; // experienced drivers + time_t duration_sec_max; // inexperienced drivers +} job_offer; + +typedef struct t_employee +{ + u32 id; + char name[MAX_EMPLOYEE_NAME_LENGTH]; + u8 age; + struct tm hire_date; + u8 experience; + float salary; + float happiness; + s16 days_below_happiness_treshold; + u32 current_location_id; + u32 original_location_id; + truck* assigned_truck; + u32 active_job_id; + + // Portrait + u8 portrait_hair_type; + color hair_color; + color face_color; + color body_color; +} employee; + +#define RESUME_FADEOUT_MS 300 +typedef struct t_resume +{ + employee* employee; + s64 expire_date; + bool hired; + animation animation; +} resume; + +typedef struct t_scheduled_job_time +{ + s16 day; // sunday = 0 + s16 timeslot; + bool stay_at_destination; + employee* assignee; +} scheduled_job_time; + +typedef struct t_scheduled_job +{ + float trust; + job_offer offer; + world_location* location; + scheduled_job_time timeslots[MAX_SHIPDAYS]; +} scheduled_job; + +typedef struct t_active_job +{ + // save state + s16 day; + s16 timeslot; + bool stay_at_destination; + job_offer offer; + employee assignee; + truck assigned_truck; + time_t duration_sec; + time_t left_at; + time_t done_at; + bool reversed; + + // dynamic + vec2f px_pos; + bool is_hovered; +} active_job; + +typedef struct t_active_job_ref +{ + s16 day; + s16 timeslot; + u32 offerid; +} active_job_ref; + +typedef struct t_world_update_result +{ + active_job* clicked_job; + world_location* clicked_location; +} world_update_result; + +#define NUM_DAYS 7 +typedef struct t_schedule +{ + array jobs; +} schedule; + +typedef struct t_world_location +{ + // Static + u8 size; + double latitude; + double longitude; + char name[MAX_WORLD_LOCATION_NAME_LENGTH]; + s32 map_position_x; + s32 map_position_y; + + // Save State + bool is_owned; + array employees; // Contains internal and external employees. Employee can be at 2 locations at any given time. + array job_offers; + array resumes; + array trucks; + float reliability; + schedule schedule; + u16 purchase_year; + array insights; + + // Dynamic + array connections; + bool is_hovered; + u32 id; +} world_location; + +typedef struct t_job_endpoints +{ + world_location* source; + world_location* dest; +} job_endpoints; + +typedef enum t_event_type +{ + EVENT_TYPE_MISSED_SHIPMENT_NO_TRUCK, // go to employee detail + EVENT_TYPE_MISSED_SHIPMENT_NOT_AT_LOCATION, // go to schedule + EVENT_TYPE_MISSED_SHIPMENT_NO_ASSIGNEE, // go to schedule + EVENT_TYPE_EMPLOYEE_QUIT, // go to schedule +} event_type; + +#define MAX_EVENT_MESSAGE_LENGTH 150 + +typedef struct t_event +{ + void* data; + event_type type; + scheduled_job_time job_time; + char message[MAX_EVENT_MESSAGE_LENGTH]; +} event; + +#define MIN_SIMULATION_SPEED 0 +#define MAX_SIMULATION_SPEED 8 + +#define LOG_HISTORY_LENGTH 25 + +typedef struct t_event_log +{ + array events; + u16 write_cursor; + bool has_unread_messages; +} event_log; + +#define ADD_EXPENSE(_world,_loc,_var,_amount)\ + {\ + _world->money -= _amount;\ + money_data_collection* collection = get_current_insights_data(_world);\ + collection->months[_world->current_time.tm_mon].total_expenses -= _amount;\ + collection->months[_world->current_time.tm_mon].total_profit -= _amount;\ + collection->months[_world->current_time.tm_mon]._var -= _amount;\ + if (_loc) {\ + collection = get_current_insights_data_for_location(_world,_loc);\ + collection->months[_world->current_time.tm_mon].total_expenses -= _amount;\ + collection->months[_world->current_time.tm_mon].total_profit -= _amount;\ + collection->months[_world->current_time.tm_mon]._var -= _amount;}\ + } + +#define ADD_INCOME(_world,_loc,_var,_amount)\ + {\ + _world->money += _amount;\ + money_data_collection* collection = get_current_insights_data(_world);\ + collection->months[_world->current_time.tm_mon].total_income += _amount;\ + collection->months[_world->current_time.tm_mon].total_profit += _amount;\ + collection->months[_world->current_time.tm_mon]._var += _amount;\ + if (_loc) {\ + collection = get_current_insights_data_for_location(_world,_loc);\ + collection->months[_world->current_time.tm_mon].total_income += _amount;\ + collection->months[_world->current_time.tm_mon].total_profit += _amount;\ + collection->months[_world->current_time.tm_mon]._var += _amount;}\ + } + +#define EXPENSES get_current_insights_data(world)->months[world->current_time.tm_mon] + +typedef struct t_money_data +{ + float total_income; + float total_expenses; + float total_profit; + + float income_from_trips; + + float expenses_from_trucks; + float expenses_from_utility; + float expenses_from_healthcare; + float expenses_from_repairs; + float expenses_from_fuel; + float expenses_from_employees; +} money_data; + +#define MONTHS_IN_YEAR 12 + +typedef struct t_money_data_collection +{ + money_data months[MONTHS_IN_YEAR]; +} money_data_collection; + +typedef struct t_company_investments +{ + u32 safety; + u32 marketing; + u32 human_resources; + u32 training; + u32 legal; +} company_investments; + +typedef struct t_world +{ + // Save State + s64 simulation_time; + u16 start_year; + float money; + u32 next_id; + u8 simulation_speed; + array active_jobs; + event_log log; + array insights; + company_investments investments; + u16 days_since_last_random_event; + + // Dynamic + array locations; + array companies; + array firstnames; + array lastnames; + array truck_dealers; + array boat_routes; + struct tm current_time; +} world; + +world* world_create_new(); +void world_report_event(world* world, char* msg, event_type type, void* data); +world_location* get_world_location_by_id(world* world, s32 id); +world_location* get_world_location_by_name(world* world, char* str); +float world_location_get_price(world_location* location); +void add_truck_to_world_location(world* world, world_location* location, truck* tr); +void world_update(platform_window* window, world* world); +world_update_result world_render(platform_window* window, world* world); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ce94b9c --- /dev/null +++ b/src/main.c @@ -0,0 +1,182 @@ +#define ASSET_FONT_COUNT 25 +#define ASSET_WORKER_COUNT 4 +#define ASSET_QUEUE_COUNT 100 +#define ASSET_IMAGE_COUNT 70 +#define ASSET_SOUND_COUNT 30 +#define NUM_AUDIO_CHANNELS 8 +#define GAME_VERSION "0.1" + +#include "../project-base/src/project_base.h" + +platform_window* main_window; + +vec4 area; +float scale = 1.0f; +float zoom = 1.0f; +float camera_x = 0.0f; +float camera_y = 0.0f; +font* fnt_rd8; +font* fnt_rd12; +font* fnt_rd16; +font* fnt_rd20; +font* fnt_rd24; +font* fnt_rd28; +font* fnt_rd32; +font* fnt_rd36; +font* fnt_rd40; +font* fnt_rd44; +font* fnt_rd48; + +#include "include/settings.h" +#include "include/ui/colors.h" +#include "include/ui/animation.h" +#include "include/world.h" +#include "include/ui/portrait.h" +#include "include/scenery.h" +#include "include/data.h" +#include "include/game.h" +#include "include/tooltip.h" +#include "include/ui/panel.h" +#include "include/ui/button.h" +#include "include/ui/selectors.h" +#include "include/scenes/menu_scene.h" +#include "include/scenes/loading_scene.h" +#include "include/scenes/save_state_select.h" +#include "include/scenes/world_map.h" +#include "include/scenes/loading_world_scene.h" +#include "include/scenes/error_scene.h" +#include "include/scenes/place_detail.h" +#include "include/scenes/settings_scene.h" + +#include "music.c" +#include "world.c" +#include "data.c" +#include "game.c" +#include "scenery.c" +#include "ui/panel.c" +#include "ui/button.c" +#include "ui/animation.c" +#include "ui/portrait.c" +#include "tooltip.c" +#include "scenes/menu_scene.c" +#include "scenes/loading_scene.c" +#include "scenes/save_state_select.c" +#include "scenes/world_map.c" +#include "scenes/loading_world_scene.c" +#include "scenes/error_scene.c" +#include "scenes/place_detail.c" +#include "scenes/settings_scene.c" +#include "ui/selectors.c" + +#define CONFIG_DIRECTORY "trucker_x" + +static void draw_debug_overlay(platform_window* window) +{ + static bool enabled = false; + if (keyboard_is_key_pressed(KEY_F1)) enabled = !enabled; + if (!enabled) return; + + renderer->set_render_depth(20); + + renderer->render_rectangle(0,0,200*scale,200*scale,rgb(70,70,70)); + + font* fnt = FONT_REGULAR(SIZE_RD(area.w, 24)); + + { + char deltabuf[20]; + sprintf(deltabuf, "Frame: %.5f", frame_delta); + renderer->render_text(fnt, 10, 10, deltabuf, rgb(255,0,0)); + } + + { + char deltabuf[20]; + sprintf(deltabuf, "Game: %.5f", update_delta); + renderer->render_text(fnt, 10, 10+(fnt->px_h+2)*1, deltabuf, rgb(255,0,0)); + } + + { + char deltabuf[20]; + sprintf(deltabuf, "FPS: %.0f", 1.0f/frame_delta); + renderer->render_text(fnt, 10, 10+(fnt->px_h+2)*2, deltabuf, rgb(255,0,0)); + } + + renderer->set_render_depth(19); +} + +void update_render_game(platform_window* window) +{ + area = camera_get_target_rectangle(window); + scale = UI_SCALE(area.w); + + fnt_rd8 = FONT_REGULAR(SIZE_RDF(scale, 8)); + fnt_rd12 = FONT_REGULAR(SIZE_RDF(scale, 12)); + fnt_rd16 = FONT_REGULAR(SIZE_RDF(scale, 16)); + fnt_rd20 = FONT_REGULAR(SIZE_RDF(scale, 20)); + fnt_rd24 = FONT_REGULAR(SIZE_RDF(scale, 24)); + fnt_rd28 = FONT_REGULAR(SIZE_RDF(scale, 28)); + fnt_rd32 = FONT_REGULAR(SIZE_RDF(scale, 32)); + fnt_rd36 = FONT_REGULAR(SIZE_RDF(scale, 36)); + fnt_rd40 = FONT_REGULAR(SIZE_RDF(scale, 40)); + fnt_rd44 = FONT_REGULAR(SIZE_RDF(scale, 44)); + fnt_rd48 = FONT_REGULAR(SIZE_RDF(scale, 48)); + + #ifdef MODE_DEBUG + draw_debug_overlay(window); + #endif + + game_update(window); + game_render(window); + + update_music(); +} + +int main(int argc, char** argv) +{ + platform_init(argc, argv, CONFIG_DIRECTORY); + + #define VALIDATE_VOLUME(_vol) if (_vol < 0.0f) _vol = 0.0f; else if (_vol > 1.0f) _vol = 1.0f; + volume_global = settings_get_number_or_default("v_global", 100) / 100.0f; + volume_music = settings_get_number_or_default("v_music", 100) / 100.0f; + volume_sfx = settings_get_number_or_default("v_sfx", 100) / 100.0f; + option_vsync = settings_get_number_or_default("vsync", 1); + option_fullscreen = settings_get_number_or_default("fullscreen", 1); + VALIDATE_VOLUME(volume_global); + VALIDATE_VOLUME(volume_music); + VALIDATE_VOLUME(volume_sfx); + + s32 window_w = settings_get_number_or_default("window_w", 1280); + s32 window_h = settings_get_number_or_default("window_h", 720); + + main_window = platform_open_window("TruckerX", + window_w, window_h, + 9999, 9999, + 960, 540 + platform_get_titlebar_height(), + update_render_game, 0); + + platform_toggle_vsync(main_window, option_vsync); + if (option_fullscreen) platform_toggle_fullscreen(main_window, option_fullscreen); + + data_load(); + game_set_active_scene(GAME_STATE_LOADING); + + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_1, volume_sfx*volume_global); + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_2, volume_sfx*volume_global); + + while(platform_keep_running(main_window)) { + main_window->do_draw = true; + platform_handle_events(); + } + + settings_set_number("window_w", main_window->width); + settings_set_number("window_h", main_window->height + platform_get_titlebar_height()); + settings_set_number("v_global", volume_global*100); + settings_set_number("v_music", volume_music*100); + settings_set_number("v_sfx", volume_sfx*100); + settings_set_number("vsync", option_vsync); + settings_set_number("fullscreen", option_fullscreen); + + settings_write_to_file(); + platform_destroy(); + + return 0; +} \ No newline at end of file diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..c85b63c --- /dev/null +++ b/src/music.c @@ -0,0 +1,22 @@ + +void update_music() +{ + static s32 current_song_index = 0; + if (!audio_music_is_playing()) { + sound* snd = snd_songs[current_song_index]; + if (!snd) { + current_song_index = 0; + } + else { + if (!snd->loaded) return; + + char buf[MAX_INPUT_LENGTH]; + sprintf(buf, "Now playing: \"%s\".", (char*)snd->start_addr); + log_info(buf); + + audio_set_music_volume(volume_music*volume_global); + audio_play_sound(snd, -1); + current_song_index++; + } + } +} \ No newline at end of file diff --git a/src/scenery.c b/src/scenery.c new file mode 100644 index 0000000..9e647d6 --- /dev/null +++ b/src/scenery.c @@ -0,0 +1,53 @@ + + +static void update_render_path(world* world, boat_route *route) +{ + boat_route_point prev_point = route->points[route->current_point]; + boat_route_point next_point = route->points[route->current_point + (route->reversed ? -1 : 1)]; + + float dist = sqrt(pow(prev_point.x - next_point.x, 2) + pow(prev_point.y - next_point.y, 2)); + float time_between_points = (dist*1000000.0f); + + float percentage = route->current_point_duration/time_between_points; + + vec2f start_pos = {area.x + (prev_point.x*area.w), area.y + (prev_point.y*area.h)}; + vec2f end_pos = {area.x + (next_point.x*area.w), area.y + (next_point.y*area.h)}; + + float currx = start_pos.x - (start_pos.x - end_pos.x)*percentage; + float curry = start_pos.y - (start_pos.y - end_pos.y)*percentage; + + vec2f map_pos = {currx*zoom+camera_x, curry*zoom+camera_y}; + + float rad = atan2(end_pos.y-start_pos.y, end_pos.x-start_pos.x); + + gl_render_set_rotation(-rad); + renderer->render_image(img_boat, map_pos.x-3, map_pos.y-3, 6, 6); + gl_render_set_rotation(0.0f); + + route->current_point_duration += (frame_delta*1000.0f)*world->simulation_speed; + if (route->current_point_duration > time_between_points) { + if (!route->reversed) route->current_point++; + else route->current_point--; + route->current_point_duration = 0; + } + if (!route->reversed && route->current_point >= route->count-1) route->reversed = true; + if (route->reversed && route->current_point <= 0) route->reversed = false; +} + +void update_render_scenery(world* world) { + + renderer->render_set_scissor(main_window, area.x,area.y,area.w,area.h); + for (s32 i = 0; i < world->boat_routes.length; i++) { + boat_route* route = array_at(&world->boat_routes, i); + update_render_path(world, route); + } + renderer->render_reset_scissor(); + + +#if 0 + static s32 count = 0; + if (is_left_down() && count++ % 10 == 0) { + printf("%f, %f,\n", (_global_mouse.x-area.x)/(float)area.w, (_global_mouse.y-area.y)/(float)area.h); + } +#endif +} \ No newline at end of file diff --git a/src/scenes/error_scene.c b/src/scenes/error_scene.c new file mode 100644 index 0000000..8d4446a --- /dev/null +++ b/src/scenes/error_scene.c @@ -0,0 +1,74 @@ + +void error_scene_init() +{ + +} + +static void error_scene_draw_info(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + float vertical_pad = 20 * scale; + float horizontal_pad = vertical_pad; + float spacing = 5 * scale; + + s32 panel_h = 280 * scale; + s32 panel_item_size = (panel_h - (vertical_pad*2) - (spacing*1)) / 2; + s32 panel_w = (panel_item_size * 3) + (horizontal_pad*2) + (spacing*2); + + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // info text + { + { + char* title = "ERROR"; + font* font_title = FONT_REGULAR(SIZE_RD(area.w, 32)); + s32 text_w = renderer->calculate_text_width(font_title, title); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + (vertical_pad*2); + renderer->render_text(font_title, text_x+2, text_y+2, title, COLOR_TEXT_SHADOW); + renderer->render_text(font_title, text_x, text_y, title, COLOR_TEXT); + } + { + char* text = "An error occured, please verify\nyour game files."; + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 28)); + s32 text_w = panel_w - (horizontal_pad*4); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = screen_center_y - (font_info->px_h/2); + renderer->render_text_cutoff(font_info, text_x+2, text_y+2, text, COLOR_TEXT_SHADOW, 9999); + renderer->render_text_cutoff(font_info, text_x, text_y, text, COLOR_TEXT, 9999); + } + + } + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_item_size/3); + s32 back_y = panel_y + panel_h - (back_h/2) - 1; + + if (push_back_button(scale, back_x, back_y, back_w, back_h)) { + game_set_active_scene(GAME_STATE_MENU); + } + } +} + +void error_scene_render(platform_window* window) +{ + menu_draw_background(window); + error_scene_draw_info(window); +} + +void error_scene_update(platform_window* window) +{ + +} + +void error_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/loading_scene.c b/src/scenes/loading_scene.c new file mode 100644 index 0000000..2240aa8 --- /dev/null +++ b/src/scenes/loading_scene.c @@ -0,0 +1,85 @@ + +#define MAX_CREDITED_NAMES 5 +#define MAX_CREDIT_NAME_LENGTH 30 +#define COMPLETE_CREDIT_LENGTH (MAX_CREDIT_NAME_LENGTH*MAX_CREDITED_NAMES)+20 +char complete_credit_text[COMPLETE_CREDIT_LENGTH]; + +void loading_scene_init() +{ + strcpy(complete_credit_text, "Music by "); + + // Load names to credit. + { + platform_set_active_directory(binary_path); + + array files = array_create(sizeof(found_file)); + array filters = string_split("AUTHOR.txt"); + bool is_cancelled = false; + platform_list_files_block(&files, "data/music/", filters, true, 0, true, &is_cancelled, 0); + log_assert(files.length <= MAX_CREDITED_NAMES, "Not enough space for credited names."); + for (s32 i = 0; i < files.length; i++) + { + found_file *file = array_at(&files, i); + + if (platform_file_exists(file->path)) + { + file_content name = platform_read_file_content(file->path, "rb"); + if (name.file_error) continue; + + string_appendn(complete_credit_text, name.content, COMPLETE_CREDIT_LENGTH); + if (i != files.length-1) { + string_appendn(complete_credit_text, ", ", COMPLETE_CREDIT_LENGTH); + } + platform_destroy_file_content(&name); + } + + mem_free(file->matched_filter); + mem_free(file->path); + } + + array_destroy(&files); + array_destroy(&filters); + } +} + +void loading_scene_render(platform_window* window) +{ + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WHITE); + + font* font_reg = FONT_REGULAR(SIZE_RD(area.w, 36)); + s32 target_size = area.h/5; + s32 logo_text_pad = 20; + s32 total_height = target_size + logo_text_pad + font_reg->px_h; + + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + s32 logo_x = screen_center_x - (target_size/2); + s32 logo_y = screen_center_y - (total_height/2); + renderer->render_image(img_logo, logo_x, logo_y, target_size, target_size); + + s32 text_y = logo_y + target_size + logo_text_pad; + char* company_name = "Tar Software"; + s32 company_name_width = renderer->calculate_text_width(font_reg, company_name); + s32 text_x = screen_center_x - (company_name_width/2); + + renderer->render_text(font_reg, text_x, text_y, company_name, COLOR_TITLE); + + // Credits + font* font_s = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 credit_pad = 30*scale; + renderer->render_text(font_s, area.x + credit_pad, area.y+area.h-credit_pad-font_s->px_h, complete_credit_text, COLOR_TITLE); +} + +void loading_scene_update(platform_window* window) +{ + platform_set_cursor(window, CURSOR_LOADING); + + if (global_asset_collection.done_loading_assets) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void loading_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/loading_world_scene.c b/src/scenes/loading_world_scene.c new file mode 100644 index 0000000..3d15909 --- /dev/null +++ b/src/scenes/loading_world_scene.c @@ -0,0 +1,116 @@ + +void loading_world_scene_init() +{ + +} + +u64 load_start_stamp = 0; + +static void* start_loading_world_t(void* arg) +{ + load_start_stamp = platform_get_time(TIME_FULL, TIME_US); // Used for displaying info texts. + #ifdef MODE_DEBUG + // thread_sleep(1000*200); + #endif + + char* path = (char*)arg; + world* world_to_load = 0; + + if (path) { + // Load from file here + } + else { + world_to_load = world_create_new(); + } + + #ifdef MODE_DEBUG + u64 load_end_stamp = platform_get_time(TIME_FULL, TIME_US); + u64 elapsed_ns = load_end_stamp - load_start_stamp; + char info_msg[50]; + sprintf(info_msg, "Loaded world in %.2fms", elapsed_ns/1000.0f); + log_info(info_msg); + #endif + + if (!world_to_load) { + log_info("Failed to load world"); + game_set_active_scene(GAME_STATE_ERROR); + } + else { + world_map_set_active_world(world_to_load); + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + + return 0; +} + +void start_loading_world(char* saved_file_path) +{ + game_set_active_scene(GAME_STATE_LOADING_WORLD); + thread_start(start_loading_world_t, saved_file_path); +} + +static void loading_world_draw_info_texts(font* font, s32 center_x, s32 y) +{ + char* texts[5] = { + "(Building cities)", + "(Connecting roads)", + "(Manufacturing trucks)", + "(Finding truck drivers)", + "(This is taking very long..)", + }; + + u64 load_end_stamp = platform_get_time(TIME_FULL, TIME_US); + u64 elapsed_ns = load_end_stamp - load_start_stamp; + float elapsed_sec = elapsed_ns / 1000.0f; + int text_index = elapsed_sec / 2000.0f; // 2 sec per text. + if (text_index >= 5) text_index = 4; + + s32 text_len = renderer->calculate_text_width(font, texts[text_index]); + renderer->render_text(font, center_x - (text_len/2), y, texts[text_index], COLOR_TEXT); +} + +static void loading_world_draw_animation(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + float vertical_pad = 20 * scale; + float horizontal_pad = vertical_pad; + float spacing = 5 * scale; + + s32 panel_h = 280 * scale; + s32 panel_item_size = (panel_h - (vertical_pad*2) - (spacing*1)) / 2; + s32 panel_w = (panel_item_size * 3) + (horizontal_pad*2) + (spacing*2); + + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + s32 carwheel_size = panel_h/4; + + static float rotation = 0.0f; + rotation -= 0.05f; + gl_render_set_rotation(rotation); + renderer->render_image(img_carwheel, screen_center_x - (carwheel_size/2), screen_center_y - (carwheel_size/2), carwheel_size, carwheel_size); + gl_render_set_rotation(0.0f); + + + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 28)); + loading_world_draw_info_texts(font_info, screen_center_x, panel_y + panel_h - font_info->px_h - vertical_pad*2); +} + +void loading_world_scene_render(platform_window* window) +{ + menu_draw_background(window); + loading_world_draw_animation(window); +} + +void loading_world_scene_update(platform_window* window) +{ + +} + +void loading_world_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/menu_scene.c b/src/scenes/menu_scene.c new file mode 100644 index 0000000..73ec22d --- /dev/null +++ b/src/scenes/menu_scene.c @@ -0,0 +1,93 @@ + +void menu_scene_init() +{ + +} + +static void menu_draw_background(platform_window* window) +{ + vec4 area = camera_get_target_rectangle(window); + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WORLD_MAP_BACKGROUND); + renderer->render_image(img_world_map, area.x, area.y, area.w, area.h); +} + +static void menu_draw_options(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + s32 panel_w = 198 * scale; + s32 panel_h = 193 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + s32 button_w = 178 * scale; + s32 button_h = 37 * scale; + s32 vertical_pad = 10 * scale; + s32 pad_x = (panel_w - button_w)/2; + float pad_y = (panel_h - (vertical_pad*2) - button_h*4)/5.0f; + + if (button_render(scale, BUTTON_ENABLED, "New Game", panel_x + pad_x, vertical_pad + panel_y + pad_y*1, button_w, button_h)) + { + start_loading_world(0); // Start new world + } + + if (button_render(scale, BUTTON_ENABLED, "Continue", panel_x + pad_x, vertical_pad + panel_y + pad_y*2 + button_h*1, button_w, button_h)) + { + game_set_active_scene(GAME_STATE_SELECT_SAVE); + } + + if (button_render(scale, BUTTON_ENABLED, "Settings", panel_x + pad_x, vertical_pad + panel_y + pad_y*3 + button_h*2, button_w, button_h)) + { + game_set_active_scene(GAME_STATE_SETTINGS); + } + + if (button_render(scale, BUTTON_ENABLED, "Quit", panel_x + pad_x, vertical_pad + panel_y + pad_y*4 + button_h*3, button_w, button_h)) + { + window->is_open = false; + } +} + +static void menu_draw_title(platform_window* window) +{ + s32 panel_w = 198 * scale; + s32 panel_h = 70 * scale; + s32 panel_pad = 50 * scale; + s32 panel_x = area.x + panel_pad; + s32 panel_y = area.y + area.h - panel_h - panel_pad; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + font* font_reg = FONT_REGULAR(SIZE_RD(area.w, 44)); + font* font_sml = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 text_pad = 5*scale; + s32 total_text_h = font_reg->px_h + text_pad + font_sml->px_h; + s32 text_y = panel_y + (panel_h/2) - (total_text_h/2); + char* game_title = "TruckerX"; + char* game_version = "rev "GAME_VERSION; + s32 game_title_width = renderer->calculate_text_width(font_reg, game_title); + s32 text_x = panel_x + (panel_w/2) - (game_title_width/2); + + renderer->render_text(font_reg, text_x+2, text_y+2, game_title, COLOR_TEXT_SHADOW); + renderer->render_text(font_reg, text_x, text_y, game_title, COLOR_TEXT); + + renderer->render_text(font_sml, 10*scale + text_x+2,font_reg->px_h + text_pad + text_y+2, game_version, COLOR_TEXT_SHADOW); + renderer->render_text(font_sml, 10*scale + text_x, font_reg->px_h + text_pad + text_y, game_version, COLOR_TEXT); +} + +void menu_scene_render(platform_window* window) +{ + menu_draw_background(window); + menu_draw_options(window); + menu_draw_title(window); +} + +void menu_scene_update(platform_window* window) +{ + +} + +void menu_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/place_detail.c b/src/scenes/place_detail.c new file mode 100644 index 0000000..699fe0e --- /dev/null +++ b/src/scenes/place_detail.c @@ -0,0 +1,1771 @@ +typedef enum t_place_detail_state +{ + PLACE_DETAIL_SHOW_MAIN, + PLACE_DETAIL_SHOW_RESUMES, + PLACE_DETAIL_SHOW_DEALERS, + PLACE_DETAIL_SHOW_EMPLOYEE, + PLACE_DETAIL_SHOW_SCHEDULE, +} place_detail_state; + +typedef enum t_place_detail_info_state +{ + PLACE_DETAIL_EMPLOYEES = 0, + PLACE_DETAIL_JOBOFFERS = 1, + PLACE_DETAIL_SCHEDULE = 2, + PLACE_DETAIL_GARAGE = 3, +} place_detail_info_state; + +typedef enum t_schedule_state +{ + SCHEDULING_JOB, + RESCHEDULING_JOB, + VIEWING, +} schedule_state; + +// Animations +#define TAG_ANIMATION_DURATION 100 +#define EMPLOYEE_SELECTOR_ANIMATION_DURATION 100 +#define TRUCK_SWAP_ANIMATION_DURATION 200 +animation tag_animation = {0,0,0,1}; +animation employee_selector_animation = {0,0,0,1}; +animation truck_swap_animation = {0,0,0,1}; + +// States +world_location* _active_location; +employee* _active_employee; +scheduled_job _active_scheduling_job; +scheduled_job* _active_selected_scheduled_job; +schedule_state _active_schedule_state = VIEWING; +s32 _active_schedule_selected_job_index = 0; +place_detail_info_state selected_tab_index = PLACE_DETAIL_EMPLOYEES; +place_detail_state current_detail_state = PLACE_DETAIL_SHOW_MAIN; +static s16 selected_truck_index = 0; +static u8 active_dealer_index = 0; +#define INVALID_VAL -999 +static s32 index_of_truck = INVALID_VAL; + +typedef struct t_tab +{ + s32 x; + s32 y; + s32 w; + s32 h; + float scale; +} tab; + +void _goto_default_detail_state() +{ + index_of_truck = INVALID_VAL; + selected_truck_index = 0; + active_dealer_index = 0; + _active_employee = 0; + _active_schedule_state = VIEWING; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + _active_schedule_selected_job_index = 0; + _active_selected_scheduled_job = 0; + keyboard_set_input_text(""); + _global_keyboard.take_input = false; +} + +void place_detail_set_active_location(world_location* location) +{ + _active_location = location; + selected_tab_index = PLACE_DETAIL_EMPLOYEES; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; +} + +void place_detail_scene_init() +{ + +} + +static char* get_shipday_list_string(job_offer* offer, char* buf, s32 len) +{ + memset(buf, 0, len); + bool first = true; + if (job_offer_has_ship_day(offer, MONDAY)) { string_appendn(buf, "Mon", len); first = false; } + if (job_offer_has_ship_day(offer, TUESDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Tue", len); first = false; } + if (job_offer_has_ship_day(offer, WEDNESDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Wed", len); first = false; } + if (job_offer_has_ship_day(offer, THURSDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Thu", len); first = false; } + if (job_offer_has_ship_day(offer, FRIDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Fri", len); first = false; } + if (job_offer_has_ship_day(offer, SATURDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Sat", len); first = false; } + if (job_offer_has_ship_day(offer, SUNDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Sun", len); first = false; } + + return buf; +} + +static scheduled_job* get_scheduled_job_at_time(s32 day, s32 timeslot) +{ + for (s32 i = 0; i < _active_location->schedule.jobs.length; i++) { + scheduled_job* slot = array_at(&_active_location->schedule.jobs, i); + for (s32 x = 0; x < slot->offer.shipday_count; x++) { + if (slot->timeslots[x].day == day && slot->timeslots[x].timeslot == timeslot) return slot; + } + } + return 0; +} + +static s32 find_empty_timeslot_for_day(s32 day) +{ + log_assert(day >= 0 && day < NUM_DAYS, "Invalid day"); + for (s32 i = 0; i < TIME_SLOTS_PER_DAY; i++) { + scheduled_job* job = get_scheduled_job_at_time(day, i); + if (!job || (_active_schedule_state == RESCHEDULING_JOB && job == _active_selected_scheduled_job)) return i; + } + return -1; +} + +static s32 find_empty_timeslot_for_day_right_to_left(s32 day) +{ + log_assert(day >= 0 && day < NUM_DAYS, "Invalid day"); + for (s32 i = TIME_SLOTS_PER_DAY-1; i >= 0; i--) { + scheduled_job* job = get_scheduled_job_at_time(day, i); + if (!job || (_active_schedule_state == RESCHEDULING_JOB && job == _active_selected_scheduled_job)) return i; + } + return -1; +} + + +static scheduled_job create_empty_job_schedule(job_offer* job) +{ + scheduled_job new_job; + new_job.location = _active_location; + new_job.offer = *job; + new_job.trust = 1.0f; + for (s32 i = 0; i < MAX_SHIPDAYS; i++) { + new_job.timeslots[i] = (scheduled_job_time){-1, -1, 0, 0}; + } + for (s32 i = 0; i < job->shipday_count; i++) { + new_job.timeslots[i] = (scheduled_job_time){job->shipdays[i], find_empty_timeslot_for_day(job->shipdays[i]), 0, 0}; + } + return new_job; +} + +#define scroll_speed 15 +#define HANDLE_TAB_SCROLL\ + bool hovering_tab = (_global_mouse.y >= orig_y && _global_mouse.y <= orig_y + h \ + && _global_mouse.x >= x && _global_mouse.x <= x + w);\ + if (scroll_h > 0) {\ + if (hovering_tab) {\ + if (_global_mouse.scroll_state < 0) current_scroll += scroll_speed;\ + if (_global_mouse.scroll_state > 0) current_scroll -= scroll_speed;\ + }\ + if (current_scroll > scroll_h) current_scroll = scroll_h;\ + if (current_scroll < 0) current_scroll = 0;\ + y -= current_scroll;\ + } + +#define HANDLE_TAB_START(_count, _parts)\ + s32 item_h = 34 * tab.scale;\ + s32 total_h = (item_h+1) * (_count) + (10 * tab.scale);\ + s32 scroll_h = total_h - h;\ + s32 orig_y = y;\ + static s32 current_scroll = 0;\ + font* fnt = fnt_rd16;\ + s32 info_text_h = fnt->px_h*4;\ + scroll_h += info_text_h;\ + s32 item_part_w = (w-item_h) / _parts;\ + (void)item_part_w; + +#define HANDLE_TAB_INFO(_info, _btn_name, _next_state)\ + s32 btn_h = info_text_h/1.3;\ + s32 btn_w = btn_h*4;\ + total_h += info_text_h;\ + s32 info_text_y = y + (btn_h/2)-(fnt->px_h/2);\ + renderer->render_text(fnt, x+1, info_text_y+1, _info, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, x, info_text_y, _info, COLOR_TEXT);\ + if (_btn_name && button_render(tab.scale, true, _btn_name, x+w-btn_w-1, y, btn_w, btn_h)) {\ + current_detail_state = _next_state;\ + }\ + y += info_text_h; + +#define HANDLE_TAB_ITEM_INTERACTION(_take) HANDLE_TAB_ITEM_INTERACTIONX(_take, i); +#define HANDLE_TAB_ITEM_INTERACTIONX(_take, _count)\ + s32 item_y = y + ((item_h+1) * _count);\ + bool hovered = (_global_mouse.x >= x && _global_mouse.x <= x + w\ + && _global_mouse.y >= item_y && _global_mouse.y <= item_y + item_h\ + && hovering_tab);\ + color tint = COLOR_LIST_ENTRY_BACKGROUND;\ + if (_take && hovered) {\ + tint = COLOR_LIST_ENTRY_BACKGROUND_ACTIVE;\ + platform_set_cursor(window, CURSOR_POINTER);\ + }\ + renderer->render_rectangle(x, item_y, w, item_h, tint); + +#define TAB_ITEM_PUSH_TEXT(_buf,_width,_total)\ + s32 text_w = renderer->calculate_text_width(fnt, _buf);\ + s32 x_start = (x + item_h*2 - text_pad) + _total;\ + s32 text_x = x_start + (_width/2)-(text_w/2);\ + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2);\ + renderer->render_text(fnt, text_x+1, text_y+1, _buf, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, text_x, text_y,_buf, COLOR_TEXT);\ + renderer->render_rectangle(x_start + _width, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON);\ + _total += _width; + +#define PUSH_NOT_AVAILABLE_TEXT(_cond, _text)\ + if (_cond) {\ + font* fnt = fnt_rd20;\ + char* txt = _text;\ + s32 textw = renderer->calculate_text_width(fnt, txt);\ + renderer->render_text(fnt, x+(w/2)-(textw/2), y+(tab.h/2)-(fnt->px_h/2)-(30*scale), txt, COLOR_TEXT);\ + } + +static void place_detail_draw_job_offers(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->job_offers.length, 2); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Job offers open.", _active_location->job_offers.length); + HANDLE_TAB_INFO(info_buf, 0, 0); + + PUSH_NOT_AVAILABLE_TEXT(!_active_location->job_offers.length, "No job offers available."); + + for (s32 i = 0; i < _active_location->job_offers.length; i++) { + job_offer* offer = array_at(&_active_location->job_offers, i); + HANDLE_TAB_ITEM_INTERACTION(true); + + if (hovered && is_left_clicked()) { + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + _active_schedule_state = SCHEDULING_JOB; + _active_schedule_selected_job_index = 0; + _active_selected_scheduled_job = 0; + _active_scheduling_job = create_empty_job_schedule(offer); + + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + renderer->render_image(offer->company->logo, x + icon_pad, item_y + icon_pad, icon_s*2, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s*2 + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + float width_of_piece = 80 * tab.scale; + float price_text_w = 0; + + // price + { + char pricebuf[25]; + sprintf(pricebuf, "$%d/trip", offer->reward); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // distance + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fkm", offer->total_distance); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // dur + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fh-%.0fh", offer->duration_sec_min/3600.0f, offer->duration_sec_max/3600.0f); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // Name + { + char daybuf[50]; + char buf[200]; + sprintf(buf, "%s wants you to ship %s to %s every %s", offer->company->name, offer->product->name, + (*(world_location**)array_at(&offer->connections, offer->connections.length-1))->name, get_shipday_list_string(offer, daybuf, 50)); + s32 text_x = x + item_h*2 + price_text_w + icon_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT); + } + } +} + + +static void place_detail_draw_trucks(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->trucks.length, 5); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Trucks in garage.", _active_location->trucks.length); + HANDLE_TAB_INFO(info_buf, "Purchase", PLACE_DETAIL_SHOW_DEALERS); + + PUSH_NOT_AVAILABLE_TEXT(!_active_location->trucks.length, "No trucks in garage."); + + for (s32 i = 0; i < _active_location->trucks.length; i++) { + truck* emp = array_at(&_active_location->trucks, i); + HANDLE_TAB_ITEM_INTERACTION(false); + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + renderer->render_image(emp->logo, x + icon_pad, item_y + icon_pad, icon_s, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_WORLD_LOCATION_NAME_LENGTH + 20]; + sprintf(buffer, "%s, ID: #%d", emp->name, emp->id); + + s32 text_x = x + item_h + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // current location + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + if (emp->assigned_employee) sprintf(buffer, "Assigned to %s", emp->assigned_employee->name); + else strcpy(buffer, "No assignee"); + s32 text_x = x + item_h + item_part_w*1 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + } + } +} + +static void place_detail_draw_employees(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->employees.length+2, 4); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Employees currently on site.", _active_location->employees.length); + HANDLE_TAB_INFO(info_buf, "Hire", PLACE_DETAIL_SHOW_RESUMES); + + s32 current_index = 0; + bool show_internal = true; + do_again:; + + for (s32 i = 0; i <= _active_location->employees.length; i++) { + employee* emp = 0; + if (i > 0) { + emp = *(employee**)array_at(&_active_location->employees, i-1); + bool is_internal = (emp->original_location_id == _active_location->id); + if (show_internal != is_internal) continue; + } + + HANDLE_TAB_ITEM_INTERACTIONX(i != 0, current_index); + if (i != 0 && hovered && is_left_clicked()) { + place_detail_show_employee_detail(emp); + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + current_index++; + + if (i == 0) { + char* buffer = show_internal ? "Internal" : "External"; + s32 text_x = x + item_h; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + continue; + } + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + draw_employee_portrait(emp, x + icon_pad, item_y + icon_pad, icon_s, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + sprintf(buffer, "%s, ID: #%d", emp->name, emp->id); + s32 text_x = x + item_h + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w, item_y+(5*tab.scale), 1, item_h-(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + if (emp->assigned_truck) sprintf(buffer, "Truck: %s, #%d", emp->assigned_truck->name, emp->assigned_truck->id); + else strcpy(buffer, "No assigned truck"); + s32 text_x = x + item_h + item_part_w*1 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w*2, item_y+(5*tab.scale), 1, item_h-(10*tab.scale), COLOR_BUTTON); + } + + // current location + { + char buffer[MAX_WORLD_LOCATION_NAME_LENGTH + 20]; + if (emp->active_job_id != INVALID_ID) { + active_job* j = get_active_job_by_id(_active_world, emp->active_job_id); + job_endpoints endpoints = job_offer_get_endpoints(&j->offer); + sprintf(buffer, "Driving to %s", endpoints.dest->name); + } + else if (emp->current_location_id != _active_location->id) { // employee located at other location + sprintf(buffer, "Located at %s", get_world_location_by_id(_active_world, emp->current_location_id)->name); + } + else { // At original location + sprintf(buffer, "%s", "Idling"); + } + + s32 text_x = x + item_h + item_part_w*2 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w*3, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Happiness + { + s32 text_x = x + item_h + item_part_w*3 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + + s32 stars = employee_calculate_happiness_stars(emp); + s32 textw = renderer->render_text(fnt, text_x, text_y, "Happiness: ", COLOR_TEXT); + text_x += (scale*5); + for (s32 i = 0; i < 5; i++) + { + if (i < stars) renderer->render_image(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h); + else renderer->render_image_tint(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h, rgba(255,255,255,30)); + } + + //renderer->render_rectangle(x + item_h + item_part_w*4, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + } + + if (show_internal) { + show_internal = false; + goto do_again; + } +} + +static void render_logo_at(image* img, s32 pad, s32 x, s32 y, s32 w, s32 h) +{ + if (img->width >= img->height) + { + float s_scale = w/(float)img->width; + float new_w = w - (pad*2); + float new_h = (img->height * s_scale) - (pad*2); + float new_x = x + w/2 - new_w/2; + float new_y = y + h/2 - new_h/2; + renderer->render_image(img,new_x,new_y,new_w,new_h); + } +} + +static void place_detail_draw_active_dealer(platform_window* window, truck_dealer* dealer, float scale, s32 x, s32 y, s32 w, s32 h) +{ + button_render(scale, false, 0, x, y, w, h); + static truck_dealer* prev_dealer = 0; + if (prev_dealer != dealer) { + prev_dealer = dealer; + selected_truck_index = 0; + } + + if (selected_truck_index < 0) selected_truck_index = 0; + if (selected_truck_index >= dealer->trucks.length) selected_truck_index = dealer->trucks.length-1; + truck* tr_curr = (selected_truck_index >= 0 && selected_truck_index <= dealer->trucks.length-1) ? array_at(&dealer->trucks, selected_truck_index) : 0; + truck* tr_prev = (selected_truck_index-1 >= 0) ? array_at(&dealer->trucks, selected_truck_index-1) : 0; + truck* tr_next = (selected_truck_index+1 <= dealer->trucks.length-1) ? array_at(&dealer->trucks, selected_truck_index+1) : 0; + if (!tr_curr) return; + s32 total_img_space = (w)/3 * 2; + s32 img_w_center = total_img_space/10*5; + s32 img_w_side = total_img_space/10*2.5; + s32 pad = 40 * scale; + s32 img_x = x; + s32 img_y = y; + + static animation swap_animation = {0,0,0,0}; + static animation purchase_animation = {0,0,0,0}; + if (swap_animation.started) animation_update(&swap_animation); + if (purchase_animation.started) animation_update(&purchase_animation); + static s32 target_truck_index = 0; + + truck* tn_prev = (target_truck_index-1 >= 0) ? array_at(&dealer->trucks, target_truck_index-1) : 0; + truck* tn_next = (target_truck_index+1 <= dealer->trucks.length-1) ? array_at(&dealer->trucks, target_truck_index+1) : 0; + + // Truck images + { + s32 overlap = 50 * scale; + + s32 y_side = img_y + (h / 2) - (img_w_side/2); + s32 x_left = img_x + overlap; + s32 y_left = y_side; + s32 w_left = img_w_side; + s32 h_left = img_w_side; + + s32 x_right = img_x+img_w_side+img_w_center - overlap; + s32 y_right = y_side; + s32 w_right = img_w_side; + s32 h_right = img_w_side; + + s32 x_center = img_x+img_w_side; + s32 y_center = img_y + (h / 2) - (img_w_center/2); + s32 w_center = img_w_center; + s32 h_center = img_w_center; + + #define LI(_cx,_dx) (_cx + (_dx-_cx)*swap_animation.percentage) + #define LIP(_cx,_dx) (_cx + (_dx-_cx)*purchase_animation.percentage) + #define LI_TINT(_c,_d) rgba(255,255,255,_c + (_d-_c)*swap_animation.percentage) + #define LI_TINTP(_c,_d) rgba(255,255,255,_c + (_d-_c)*purchase_animation.percentage) + + if (tr_prev) { + if (target_truck_index < selected_truck_index) { + color tint = LI_TINT(50, 255); + renderer->render_image_tint(tr_prev->logo,LI(x_left,x_center),LI(y_left,y_center),LI(w_left,w_center),LI(h_left,h_center), tint); + + if (swap_animation.started && tn_prev) { + color tint = LI_TINT(0, 50); + renderer->render_image_tint(tn_prev->logo,x_left,y_left,w_left,h_left, tint); + } + } + else { + color tint = LI_TINT(50, 0); + renderer->render_image_tint(tr_prev->logo,x_left,y_left,w_left,h_left, tint); + } + } + if (tr_next) { + if (target_truck_index < selected_truck_index) { + color tint = LI_TINT(50, 0); + renderer->render_image_tint(tr_next->logo,x_right,y_right,w_right,h_right, tint); + } + else { + color tint = LI_TINT(50, 255); + renderer->render_image_tint(tr_next->logo,LI(x_right,x_center),LI(y_right,y_center),LI(w_right,w_center),LI(h_right,h_center), tint); + + if (swap_animation.started && tn_next) { + color tint = LI_TINT(0, 50); + renderer->render_image_tint(tn_next->logo,x_right,y_right,w_right,h_right, tint); + } + } + } + if (!purchase_animation.started) + { + color tint = LI_TINT(255, 50); + if (target_truck_index < selected_truck_index) + renderer->render_image_tint(tr_curr->logo,LI(x_center,x_right),LI(y_center,y_right),LI(w_center,w_right),LI(h_center,h_right), tint); + else + renderer->render_image_tint(tr_curr->logo,LI(x_center,x_left),LI(y_center,y_left),LI(w_center,w_left),LI(h_center,h_left), tint); + } + else { + vec4 area = camera_get_target_rectangle(window); + s32 x_off = area.x; + s32 h_off = area.h/2; + s32 y_off = area.y+(area.h/2)-(h_off/2); + s32 w_off = h_off; + + color tint = LI_TINTP(255, 0); + renderer->render_image_tint(tr_curr->logo,LIP(x_center,x_off),LIP(y_center,y_off),LIP(w_center,w_off),LIP(h_center,h_off), tint); + } + } + + font* fnt = fnt_rd24; + s32 text_y = img_y + pad; + s32 text_x = x + total_img_space; + + #define PUSH_TEXT(_str, _data)\ + {char buf[50]; snprintf(buf,50,_str,_data);\ + renderer->render_text(fnt, text_x+1, text_y+1, buf, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT);}\ + text_y+=fnt->px_h+(10*scale); + + PUSH_TEXT("Name: %s", tr_curr->name); + PUSH_TEXT("Power: %dhp", tr_curr->hp); + PUSH_TEXT("Price: $%d", tr_curr->price); + PUSH_TEXT("Fuel Capacity: %dL", tr_curr->fuelcapacity); + PUSH_TEXT("Torque %drpm", tr_curr->torque); + PUSH_TEXT("Fuel Usage %.1fL per 100Km", tr_curr->fuelusage); + + #define TRUCK_SWAP_DELAY 200 + #define TRUCK_PURCHASE_DELAY 800 + s32 btn_size = 30*scale; + s32 btn_x = text_x; + + button_type type1 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : (selected_truck_index > 0) ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type1, "<", btn_x, text_y, btn_size, btn_size)) { + if (!swap_animation.started) { + swap_animation = animation_create(TRUCK_SWAP_DELAY); + swap_animation.started = true; + target_truck_index = selected_truck_index-1; + } + } + btn_x += btn_size; + s32 center_btn_w = 100*scale; + button_type type2 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : BUTTON_ENABLED; + if (button_render(scale, type2, "purchase", btn_x, text_y, center_btn_w, btn_size)) { + ADD_EXPENSE(_active_world, _active_location, expenses_from_trucks, tr_curr->price) + add_truck_to_world_location(_active_world, _active_location, tr_curr); + purchase_animation = animation_create(TRUCK_PURCHASE_DELAY); + purchase_animation.started = true; + } + btn_x += center_btn_w; + button_type type3 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : (selected_truck_index < dealer->trucks.length-1) ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type3, ">", btn_x, text_y, btn_size, btn_size)) { + if (!swap_animation.started) { + swap_animation = animation_create(TRUCK_SWAP_DELAY); + swap_animation.started = true; + target_truck_index = selected_truck_index+1; + } + } + if (swap_animation.percentage == 1.0f) { + swap_animation = (animation){0,0,0,0}; + selected_truck_index = target_truck_index; + } + if (purchase_animation.percentage == 1.0f) { + purchase_animation = (animation){0,0,0,0}; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + selected_truck_index = 0; + active_dealer_index = 0; + } +} + +void place_detail_show_schedule_with_highlighted_job(world_location* loc, scheduled_job* job, scheduled_job_time job_time) { + _goto_default_detail_state(); + place_detail_set_active_location(loc); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + + if (job) { + for (s32 i = 0; i < MAX_SHIPDAYS; i++) { + if (job_time.day == job->timeslots[i].day) { + _active_schedule_selected_job_index = i; + break; + } + } + } + + _active_schedule_state = VIEWING; + _active_selected_scheduled_job = job; + selected_tab_index = PLACE_DETAIL_SHOW_MAIN; + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; +} + +void place_detail_show_employee_detail(employee* emp) { + _goto_default_detail_state(); + world_location* original_loc = get_world_location_by_id(_active_world, emp->original_location_id); + place_detail_set_active_location(original_loc); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + current_detail_state = PLACE_DETAIL_SHOW_EMPLOYEE; + _active_employee = emp; + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; +} + +static s32 place_detail_draw_selected_employee_truck_selector(platform_window* window, s32 x, s32 y, s32 w, s32 h) +{ + s32 btn_w = 40*scale; + s32 pad = 30*scale; + s32 truck_h = h / 2; + s32 truck_w = truck_h; + s32 truck_x = x + (pad*2) + btn_w; + s32 truck_y = y + pad; + + world_location* original_location = get_world_location_by_id(_active_world, _active_employee->original_location_id); + + if (index_of_truck == INVALID_VAL) { + index_of_truck = _active_employee->assigned_truck ? ((void*)_active_employee->assigned_truck - (void*)original_location->trucks.data) / sizeof(truck) : -1; + } + + s32 bg_h = h; + s32 bg_w = truck_w + (btn_w*2) + (pad*4); + button_draw_background(scale, x,y,bg_w,bg_h, COLOR_WHITE, COLOR_BUTTON); + + static bool animation_going_left = false; + + // Draw truck info + { + #define TRUCK_ANIMATION_OFFSET (animation_going_left ? -60*scale : 60*scale) + s32 truck_offset_x = TRUCK_ANIMATION_OFFSET - (TRUCK_ANIMATION_OFFSET*truck_swap_animation.percentage); + color truck_tint = AN_LI_TINT(COLOR_WHITE, truck_swap_animation); + + truck* selected_truck = 0; + if (index_of_truck != -1) selected_truck = array_at(&original_location->trucks, index_of_truck); + renderer->render_image_tint(selected_truck ? selected_truck->logo : img_truck_unknown, truck_x+truck_offset_x, truck_y, truck_w, truck_h, truck_tint); + if (selected_truck) { + font* fnt = fnt_rd24; + char buf[40]; + sprintf(buf, "%s, ID: #%d", selected_truck->name, selected_truck->id); + s32 text_w = renderer->calculate_text_width(fnt, buf); + s32 text_x = truck_x + (truck_w/2)-(text_w/2); + renderer->render_text(fnt, text_x+1+truck_offset_x, truck_y+truck_h+1, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x+truck_offset_x,truck_y+truck_h, buf, COLOR_TEXT); + } + } + + // Change truck + { + if (button_render(scale, BUTTON_ENABLED, "<", truck_x-btn_w-pad,truck_y, btn_w, truck_h)) { + s32 prev_index = index_of_truck; + index_of_truck = -1; + + try_again: + for (s32 i = prev_index-1; i >= 0; i--) { + truck* emp = array_at(&original_location->trucks, i); + if (emp->assigned_employee == 0 || emp->assigned_employee == _active_employee) { + index_of_truck = i; + break; + } + } + + if (prev_index == -1) { + prev_index = original_location->trucks.length; + goto try_again; + } + + truck_swap_animation = animation_create(TRUCK_SWAP_ANIMATION_DURATION); + truck_swap_animation.started = true; + animation_going_left = true; + } + if (button_render(scale, BUTTON_ENABLED, ">", truck_x+truck_w+pad,truck_y, btn_w, truck_h)) { + s32 prev_index = index_of_truck; + index_of_truck = -1; + for (s32 i = prev_index+1; i < original_location->trucks.length; i++) { + truck* emp = array_at(&original_location->trucks, i); + if (emp->assigned_employee == 0 || emp->assigned_employee == _active_employee) { + index_of_truck = i; + break; + } + } + + truck_swap_animation = animation_create(TRUCK_SWAP_ANIMATION_DURATION); + truck_swap_animation.started = true; + animation_going_left = false; + } + } + + + // Save changes button + { + s32 btn_h = 30*scale; + btn_w = 160*scale; + s32 btn_y = y + h - pad - btn_h; + s32 btn_x = x + (bg_w/2) - (btn_w/2); + if (button_render(scale, BUTTON_ENABLED, "Save Changes", btn_x, btn_y, btn_w, btn_h)) { + if (index_of_truck != -1) { + if (_active_employee->assigned_truck) _active_employee->assigned_truck->assigned_employee = 0; + _active_employee->assigned_truck = array_at(&_active_location->trucks, index_of_truck); + log_assert(_active_employee->assigned_truck->assigned_employee == 0 && + _active_employee->assigned_truck->assigned_employee != _active_employee, "Truck already has assignee"); + + _active_employee->assigned_truck->assigned_employee = _active_employee; + } + else { + if (_active_employee->assigned_truck) _active_employee->assigned_truck->assigned_employee = 0; + _active_employee->assigned_truck = 0; + } + + index_of_truck = INVALID_VAL; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + } + + animation_update(&truck_swap_animation); + return bg_w; +} + +static void place_detail_draw_selected_employee(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + + s32 truck_selector_w = place_detail_draw_selected_employee_truck_selector(window, x, y, w, h); + + { + font* fnt = fnt_rd24; + + s32 info_panel_x = x + truck_selector_w+offset; + s32 info_panel_w = w - offset - truck_selector_w; + s32 pad = 30*scale; + + s32 text_y = y + pad; + s32 text_x = info_panel_x + pad; + s32 text_offset = fnt->px_h + (10*scale); + s32 info_h = (h/2)-(pad/2); + button_draw_background(scale, info_panel_x, y, info_panel_w, info_h, COLOR_WHITE, COLOR_BUTTON); + + s32 portrait_h = (info_h)-(pad*2); + draw_employee_portrait(_active_employee, text_x, text_y, portrait_h, portrait_h); + + text_x += portrait_h + pad; + + #define PUSH_INFO_TEXT(_fmt, _data)\ + {\ + char buf[100];\ + sprintf(buf, _fmt, _data);\ + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT);\ + text_y += text_offset;\ + } + + PUSH_INFO_TEXT("Name: %s", _active_employee->name); + PUSH_INFO_TEXT("Age: %d", _active_employee->age); + PUSH_INFO_TEXT("Experience: %d years", _active_employee->experience); + PUSH_INFO_TEXT("Salary: $%.2f/month", _active_employee->salary); + + // Happiness + { + s32 stars = employee_calculate_happiness_stars(_active_employee); + s32 textw = renderer->render_text(fnt, text_x, text_y, "Happiness: ", COLOR_TEXT); + for (s32 i = 0; i < 5; i++) + { + if (i < stars) renderer->render_image(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h); + else renderer->render_image_tint(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h, rgba(255,255,255,30)); + } + text_y += text_offset; + } + + #define SALARY_RAISE_INCREASE 70 + // Buttons + { + s32 pad_between_items = 10 * scale; + s32 button_w = 164 * scale; + s32 button_h = 37 * scale; + s32 btn_y = y+info_h+pad+info_h-button_h; + s32 btn_x = x + w - button_w; + if (button_render(scale, BUTTON_ENABLED, "End Contract", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click3, AUDIO_CHANNEL_SFX_1); + end_contract_with_employee(_active_world, _active_employee); + _active_employee = 0; + _goto_default_detail_state(); + } + btn_y -= (button_h+pad_between_items); + + if (button_render(scale, BUTTON_ENABLED, "Give Raise", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + _active_employee->salary += SALARY_RAISE_INCREASE; + } + btn_y -= (button_h+pad_between_items); + } + } +} + +static void place_detail_draw_schedule_employee_options(platform_window* window, s32 pad, float scale, s32 x, s32 y, s32 w, s32 h) +{ + bool viewing = (_active_schedule_state == VIEWING && !_active_selected_scheduled_job); + if (viewing) return; + + job_endpoints endpoints = job_offer_get_endpoints(&_active_scheduling_job.offer); + bool enabled = endpoints.dest->is_owned; + + if (enabled) button_draw_background(scale, x,y,w,h, COLOR_WHITE, COLOR_BUTTON); + else button_draw_background(scale, x,y,w,h, COLOR_BUTTON_DISABLED_TINT, COLOR_BUTTON_DISABLED); + + s32 checkbox_s = h/3; + scheduled_job_time selected_timeslot = _active_scheduling_job.timeslots[_active_schedule_selected_job_index]; + if (_active_schedule_state == VIEWING) selected_timeslot = _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index]; + + char* check_txt = selected_timeslot.stay_at_destination ? "X" : ""; + if (button_render(scale, enabled && _active_schedule_state != VIEWING ? BUTTON_ENABLED : BUTTON_DISABLED, check_txt, x+pad,y+(h/2)-(checkbox_s/2),checkbox_s,checkbox_s)) { + _active_scheduling_job.timeslots[_active_schedule_selected_job_index].stay_at_destination = !selected_timeslot.stay_at_destination; + } + + font* fnt = fnt_rd20; + s32 textx = x + pad + checkbox_s + pad/2; + s32 texty = y+(h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, "Stay at destination", COLOR_TEXT); + + if (!enabled) { + s32 icon_s = h/2; + renderer->render_image(img_lock, x + (w/2)-(icon_s/2), y + (h/2)-(icon_s/2), icon_s, icon_s); + } +} + +static void place_detail_draw_schedule_employee_selector(platform_window* window, s32 pad, float scale, s32 x, s32 y, s32 w, s32 h) +{ + if (_active_schedule_state == VIEWING && !_active_selected_scheduled_job) { + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + font* fnt = fnt_rd20; + s32 texty = y + (h/2)-(fnt->px_h/2); + s32 textx = x + pad; + renderer->render_text(fnt, textx, texty, "Select a timeslot for details.", COLOR_TEXT); + } + else { + scheduled_job_time selected_timeslot = _active_scheduling_job.timeslots[_active_schedule_selected_job_index]; + + employee* selected_employee = selected_timeslot.assignee; + bool is_viewing_existing_timeslot = _active_selected_scheduled_job && _active_schedule_state == VIEWING; + if (is_viewing_existing_timeslot) { + selected_employee = _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index].assignee; + } + + // When timeslot changes, set selected employee id as keyboard input text. + static s32 prev_selected_job_index = 0; + if (is_viewing_existing_timeslot) prev_selected_job_index = -1; + if (prev_selected_job_index != _active_schedule_selected_job_index) { + prev_selected_job_index = _active_schedule_selected_job_index; + if (selected_employee) { + char idbuf[20]; + sprintf(idbuf, "%d", selected_employee->id); + keyboard_set_input_text(idbuf); + } + else { + keyboard_set_input_text(""); + } + } + + _active_scheduling_job.timeslots[_active_schedule_selected_job_index].assignee = + employee_selector_render(window, scale, !is_viewing_existing_timeslot, selected_employee,x,y,w,h, employee_selector_animation, &_active_scheduling_job); + animation_update(&employee_selector_animation); + } +} + +static bool scheduling_job_is_correct() +{ + for (s32 i = 0; i < _active_scheduling_job.offer.shipday_count; i++) { + scheduled_job_time scheduled_time = _active_scheduling_job.timeslots[i]; + if (!scheduled_time.assignee) { return false; } + if (scheduled_time.timeslot == -1) { return false; } + } + return true; +} + +static void draw_duration_of_scheduled_job_entry(scheduled_job_time scheduled_time, s32 hour_w, s32 tx, s32 ty, s32 timeslot_w, s32 x, s32 y, s32 timeslot_h, color tile_color) +{ + #define TILE_X(_x) (x+hour_w+(timeslot_w*_x)+1) + #define TILE_Y(_y) (y+(timeslot_h*(CDAYTORDAY(_y)))+1) + + s32 duration_of_job_measured_in_timeslots_max = ceil((_active_scheduling_job.offer.duration_sec_max/60.0f) / (60.0f/TIME_SLOTS_PER_HOUR)); + if (!scheduled_time.stay_at_destination) duration_of_job_measured_in_timeslots_max*=2; + log_assert(duration_of_job_measured_in_timeslots_max < 36*TIME_SLOTS_PER_HOUR, "Multi-day trip not supported yet in schedule preview."); + + s32 slots_outside_of_schedule = (24-(WORK_HOUR_END-WORK_HOUR_START))*TIME_SLOTS_PER_HOUR; + s32 overflow_to_next_day = -(((TIME_SLOTS_PER_DAY-scheduled_time.timeslot) - + (duration_of_job_measured_in_timeslots_max)) + + slots_outside_of_schedule); + color tile_color_bg = tile_color; + tile_color_bg.a = 50; + + s32 highlight_w = timeslot_w*duration_of_job_measured_in_timeslots_max; + if (tx+highlight_w > x+(timeslot_w*(TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR))) + highlight_w -= (tx+highlight_w) - (x+(timeslot_w*(TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR))); + renderer->render_rectangle(tx, ty, highlight_w, timeslot_h, tile_color_bg); + s32 highlight_y = ty+timeslot_h; + if (scheduled_time.day == SUNDAY) highlight_y = TILE_Y(MONDAY); + if (overflow_to_next_day > 0) renderer->render_rectangle(TILE_X(0), highlight_y, timeslot_w*overflow_to_next_day, timeslot_h, tile_color_bg); +} + +static void place_detail_draw_schedule(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 pad_between_items = 10 * scale; + s32 employee_select_h = (h/4); + h -= employee_select_h + pad_between_items; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + s32 btn_pad = pad_between_items/2; + + s32 button_w = 124 * scale; + s32 button_h = (employee_select_h-btn_pad)/2; + s32 schedule_pad = 20*scale; + + #define COLS (TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR) + #define ROWS 8 + s32 timeslot_w = (w-schedule_pad*2)/COLS; + s32 timeslot_h = (h-schedule_pad*2)/ROWS; + s32 hour_w = timeslot_w*4; + s32 new_w = (timeslot_w)*COLS+schedule_pad*2; + s32 new_h = (timeslot_h)*ROWS+schedule_pad*2; + s32 off_x = w - new_w; + s32 off_y = h - new_h; + w = new_w; + h = new_h; + x += off_x/2; + y += off_y/2; + + s32 employee_select_y = y + h + pad_between_items; + s32 btn_y = y+h+pad_between_items; + if (_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) + { + button_type type = scheduling_job_is_correct() ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type, "Accept", x-button_w+w, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + if (_active_schedule_state == SCHEDULING_JOB) + array_push(&_active_location->schedule.jobs, &_active_scheduling_job); + else if (_active_schedule_state == RESCHEDULING_JOB) + *_active_selected_scheduled_job = _active_scheduling_job; + + job_offer* offer = get_job_offer_by_id(_active_location, _active_scheduling_job.offer.id); + if (offer) array_remove_by(&_active_location->job_offers, offer); + + _goto_default_detail_state(); + } + btn_y += btn_pad + button_h; + if (button_render(scale, BUTTON_ENABLED, "Cancel", x-button_w+w, btn_y, button_w, button_h)) { + _goto_default_detail_state(); + } + } + else if (_active_schedule_state == VIEWING && _active_selected_scheduled_job) { + if (button_render(scale, BUTTON_ENABLED, "Reschedule", x-button_w+w, btn_y, button_w, button_h)) { + _active_schedule_state = RESCHEDULING_JOB; + _active_scheduling_job = *_active_selected_scheduled_job; + } + } + + s32 options_w = 230*scale; + s32 selector_w = w-options_w-(pad_between_items*2)-button_w; + + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + + x+=schedule_pad; + y+=schedule_pad; + w-=schedule_pad*2; + h-=schedule_pad*2; + + #define TILE_X(_x) (x+hour_w+(timeslot_w*_x)+1) + #define TILE_Y(_y) (y+(timeslot_h*(CDAYTORDAY(_y)))+1) + + + vec2f hovered_tile = (vec2f){(s32)((_global_mouse.x-x)/timeslot_w)-TIME_SLOTS_PER_HOUR, (s32)((_global_mouse.y-y)/timeslot_h)}; + s32 hovered_day = RDAYTOCDAY((s32)hovered_tile.y); + scheduled_job* existing_job_at_hovered_tile = get_scheduled_job_at_time(hovered_day, (s32)hovered_tile.x); + font* fnt = fnt_rd24; + + static bool dragging_tile = false; + + // newly scheduling job + if (_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) + { + for (s32 i = 0; i < _active_scheduling_job.offer.shipday_count; i++) { + scheduled_job_time scheduled_time = _active_scheduling_job.timeslots[i]; + s32 dday = CDAYTORDAY(scheduled_time.day); + renderer->render_rectangle(x+hour_w, y+(dday*timeslot_h), w-hour_w, timeslot_h, COLOR_SCHEDULE_ROW_ACTIVE); + + bool being_hovered = false; + bool valid_tile = (!existing_job_at_hovered_tile || (_active_schedule_state == RESCHEDULING_JOB && existing_job_at_hovered_tile == _active_selected_scheduled_job)); + if (valid_tile && hovered_tile.x >= 0 && hovered_tile.x < TIME_SLOTS_PER_DAY && hovered_tile.y == dday) { // If in current row. + + // Interaction. + if (_active_scheduling_job.timeslots[i].timeslot == hovered_tile.x) { + platform_set_cursor(window, CURSOR_POINTER); + being_hovered = true; + + // Check if player started to drag. + if (is_left_clicked_peak()) { // is_left_clicked_peak instead of is_left_clicked so employee selector can reset input. + dragging_tile = true; + _active_schedule_selected_job_index = i; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + } + } + + s32 tx = TILE_X(scheduled_time.timeslot); + s32 ty = TILE_Y(scheduled_time.day); + + bool is_dragging_current_tile = (dragging_tile && _active_schedule_selected_job_index == i); + color tile_color = (_active_schedule_selected_job_index == i) ? COLOR_SCHEDULE_TILE_HOVERED : COLOR_SCHEDULE_TILE_SELECTED; + + // Handle dragging. + if (being_hovered || is_dragging_current_tile) { + + // Get index of new timeslot. + s32 index_to_set = hovered_tile.x; + s32 possible_tile_left = find_empty_timeslot_for_day(scheduled_time.day); + s32 possible_tile_right = find_empty_timeslot_for_day_right_to_left(scheduled_time.day); + if (index_to_set < 0) index_to_set = possible_tile_left; + if (index_to_set >= TIME_SLOTS_PER_DAY) index_to_set = possible_tile_right; + + // Set new timeslot. + if (is_dragging_current_tile) { + platform_set_cursor(window, CURSOR_DRAG); + + scheduled_job* existing_job_at_tile = get_scheduled_job_at_time(scheduled_time.day, index_to_set); + if (existing_job_at_tile == _active_selected_scheduled_job) existing_job_at_tile = 0; // Make sure reschedule can be set at current location. + if (index_to_set != -1 && !existing_job_at_tile) _active_scheduling_job.timeslots[i].timeslot = index_to_set; + + if (!is_left_down()) { + dragging_tile = false; + } + + tx = TILE_X(_active_scheduling_job.timeslots[i].timeslot); + ty = TILE_Y(scheduled_time.day); + } + + // Draw duration of job. + draw_duration_of_scheduled_job_entry(scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tile_color); + + // Draw left and right arrow. + s32 icon_s = timeslot_w*0.7f; + s32 icon_y = ty + (timeslot_h-icon_s)/2; + s32 icon_pad = (3*scale); + if (possible_tile_left != -1 && possible_tile_left != index_to_set) { + renderer->render_image_tint(img_arrow_left_rounded, tx - icon_s - icon_pad-1, icon_y, icon_s+2, icon_s,COLOR_SCHEDULE_BORDER_THIN); + renderer->render_image_tint(img_arrow_left_rounded, tx - icon_s - icon_pad, icon_y, icon_s, icon_s,tile_color); + } + if (possible_tile_right != -1 && possible_tile_right != index_to_set) { + gl_render_set_rotation(M_PI); + renderer->render_image_tint(img_arrow_left_rounded, tx + timeslot_w + icon_pad-1, icon_y, icon_s+2, icon_s,COLOR_SCHEDULE_BORDER_THIN); + renderer->render_image_tint(img_arrow_left_rounded, tx + timeslot_w + icon_pad, icon_y, icon_s, icon_s,tile_color); + gl_render_set_rotation(0.0f); + } + } + + // Draw tile. + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, tile_color); + + // Status icon if tile being scheduled. + s32 icon_size = timeslot_w*0.6f; + if (scheduled_time.assignee == 0) { + renderer->render_image(img_questionmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + else { + renderer->render_image(img_checkmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + } + } + + // existing jobs + { + for (s32 i = 0; i < _active_location->schedule.jobs.length; i++) { + scheduled_job* job = array_at(&_active_location->schedule.jobs, i); + + // if rescheduling, dont show the timeslots for the job being rescheduled. + if (_active_schedule_state == RESCHEDULING_JOB && _active_selected_scheduled_job == job) { + continue; + } + + for (s32 t = 0; t < job->offer.shipday_count; t++) { + scheduled_job_time scheduled_time = job->timeslots[t]; + s32 tx = TILE_X(scheduled_time.timeslot); + s32 ty = TILE_Y(scheduled_time.day); + bool is_selected_job = (_active_selected_scheduled_job == job); + bool is_selected = (is_selected_job && _active_schedule_selected_job_index == t); + bool is_inspecting_employee = false; + + if (_active_selected_scheduled_job != 0 && scheduled_time.assignee == _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index].assignee && !is_selected) { + is_inspecting_employee = true; + } + + color tc = COLOR_SCHEDULE_TILE_FIXED; + if (is_inspecting_employee) { + tc = COLOR_SCHEDULE_TILE_HIGHLIGHTED; + } + else if (is_selected) { + tc = COLOR_SCHEDULE_TILE_HOVERED; + } + if (scheduled_time.assignee == 0) tc = is_selected ? COLOR_SCHEDULE_TILE_HOVERED : COLOR_SCHEDULE_TILE_INVALID; + if (is_inspecting_employee || is_selected) { + // Draw duration of job. + draw_duration_of_scheduled_job_entry(scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tc); + } + + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, tc); + if (is_selected_job && !is_selected) { + s32 dotsize = timeslot_w/3; + s32 dotoffsetx = (timeslot_w/2)-(dotsize/2); + s32 dotoffsety = timeslot_h-(timeslot_h/4)-(dotsize/2); + renderer->render_image_tint(img_dot, tx+dotoffsetx,ty+dotoffsety,dotsize,dotsize,COLOR_SCHEDULE_TILE_HOVERED); + } + + // Status icon + s32 icon_size = timeslot_w*0.6f; + if (scheduled_time.assignee == 0) { + renderer->render_image(img_questionmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + + #if 0 + color col = COLOR_SCHEDULE_TILE_FIXED; + if (is_selected_job) col = COLOR_SCHEDULE_TILE_HIGHLIGHTED; + if (is_selected) col = COLOR_SCHEDULE_TILE_HOVERED; + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, col); + #endif + + #if 0 + // Viewing job info box. + if (is_selected) { + s32 tooltip_offset_from_tile = 15*scale; + s32 tooltip_pad = 15*scale; + s32 tooltip_h = fnt_s->px_h + (tooltip_pad*2); + s32 tooltip_x = tx + timeslot_w + tooltip_offset_from_tile; + s32 tooltip_y = ty + (timeslot_h/2)-(tooltip_h/2); + // if (tx > area.x + (area.w/2)) + + char* tooltip_text = "Maastricht -> Machester"; + s32 tooltip_text_w = renderer->calculate_text_width(fnt_s, tooltip_text); + s32 tooltip_w = tooltip_text_w + (tooltip_pad*2); + + renderer->set_render_depth(5); + button_draw_background(scale, tooltip_x,tooltip_y,tooltip_w,tooltip_h, COLOR_WHITE, COLOR_BUTTON); + renderer->render_text(fnt_s, tooltip_x+tooltip_pad, tooltip_y+tooltip_pad, tooltip_text, COLOR_TEXT); + renderer->set_render_depth(4); + } + #endif + + if (_active_schedule_state == VIEWING && mouse_interacts(tx,ty,timeslot_w,timeslot_h)) { + platform_set_cursor(window, CURSOR_POINTER); + + if (is_left_clicked()) { + scheduled_job* prev_selected_scheduled_job = _active_selected_scheduled_job; + _active_schedule_state = VIEWING; + _active_selected_scheduled_job = job; + _active_schedule_selected_job_index = t; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + + if (_active_selected_scheduled_job != prev_selected_scheduled_job) { + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; + } + + employee_selector_animation = animation_create(EMPLOYEE_SELECTOR_ANIMATION_DURATION); + employee_selector_animation.started = true; + } + } + } + } + } + + // rows + for (s32 ty = 0; ty < ROWS; ty++) + { + s32 row_y = y + (ty*timeslot_h); + + if (ty==0) { + // COLOR_SCHEDULE_ROW_ACTIVE + renderer->render_rectangle(x, y, timeslot_w*4, h, COLOR_SCHEDULE_BG); + renderer->render_rectangle(x, y, w, timeslot_h, COLOR_SCHEDULE_BG); + } + else { + // Days + { + char buf[20]; + switch(ty) { + case 1: strcpy(buf, "Mon"); break; + case 2: strcpy(buf, "Tue"); break; + case 3: strcpy(buf, "Wed"); break; + case 4: strcpy(buf, "Thu"); break; + case 5: strcpy(buf, "Fri"); break; + case 6: strcpy(buf, "Sat"); break; + case 7: strcpy(buf, "Sun"); break; + } + + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = x + (hour_w/2)-(tw/2); + s32 texty = row_y + (timeslot_h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, buf, COLOR_TEXT); + } + } + renderer->render_rectangle(x, row_y, w, 1, COLOR_SCHEDULE_BORDER); + } + + // cols + for (s32 tx = 0; tx < COLS; tx++) + { + s32 row_x = x + (tx*timeslot_w); + bool is_big_line = tx % 4 == 0; + if (tx>=4 || is_big_line) { + s32 liney = y; + s32 lineh = h; + if (!is_big_line) { + liney = y+timeslot_h; + lineh = h-timeslot_h; + } + renderer->render_rectangle(row_x, liney, 1, lineh, (is_big_line) ? COLOR_SCHEDULE_BORDER : COLOR_SCHEDULE_BORDER_THIN); + } + if (tx!=0 && is_big_line) { + + char buf[20]; + sprintf(buf, "%d:00", WORK_HOUR_START+((tx-1)/TIME_SLOTS_PER_HOUR)); + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = row_x + (hour_w/2)-(tw/2); + s32 texty = y + (timeslot_h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, buf, COLOR_TEXT); + } + } + + // outline + renderer->render_rectangle_outline(x,y,w+1,h+1,1,COLOR_SCHEDULE_BORDER); + + x -= schedule_pad; + place_detail_draw_schedule_employee_options(window, schedule_pad, scale, x + selector_w + pad_between_items, employee_select_y, options_w, employee_select_h); + place_detail_draw_schedule_employee_selector(window, schedule_pad, scale, x, employee_select_y, selector_w, employee_select_h); + + // Deselect selected job. + if (mouse_interacts(x+schedule_pad,y,w+1,h+1) && is_left_clicked()) { + if (_active_schedule_state == VIEWING) { + _active_selected_scheduled_job = 0; + } + } +} + +static void place_detail_draw_dealers(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + + s32 item_count = _active_world->truck_dealers.length; + + s32 spacing = 6; + s32 logo_space_w = w / 7.0f; + s32 logo_space_h = (h-(spacing*(item_count-1))) / 3.0f; + + for (s32 i = 0; i < item_count; i++) + { + truck_dealer* d = array_at(&_active_world->truck_dealers, i); + if(button_render(scale, true, 0, x, y + (logo_space_h*i)+spacing*i, logo_space_w, logo_space_h)) active_dealer_index = i; + render_logo_at(d->logo, 40 * scale, x, y + (logo_space_h*i)+spacing*i, logo_space_w, logo_space_h); + } + + if (active_dealer_index < _active_world->truck_dealers.length) { + truck_dealer* d = array_at(&_active_world->truck_dealers, active_dealer_index); + + s32 panel_x = x + spacing + logo_space_w; + s32 panel_w = (x + w) - panel_x; + place_detail_draw_active_dealer(window, d, scale, panel_x, y, panel_w, h); + } +} + +static void place_detail_draw_resumes(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + s32 panel_w = area.w*0.75; + s32 panel_h = area.h*0.75; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + + bool resume_available = _active_location->resumes.length; + + font* fntb = fnt_rd32; + font* fnt = fnt_rd16; + + if (!resume_available) { + char* message = "No resumes available."; + s32 text_w = renderer->calculate_text_width(fnt, message); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = screen_center_y - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, message, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, message, COLOR_TEXT); + } + else { + s32 panel_w = area.w*0.2; + s32 panel_h = area.h*0.3; + panel_x = screen_center_x - (panel_w/2); + panel_y = screen_center_y - (panel_h/2); + resume* resume = array_at(&_active_location->resumes, 0); + + float opacity = 1.0f - resume->animation.percentage; + if (opacity < 0.0f) opacity = 0.0f; + + color color_bg = COLOR_WHITE; + color_bg.a = opacity*255; + + renderer->render_image_tint(img_resume, panel_x, panel_y, panel_w, panel_h, color_bg); + + char buffer[100]; + sprintf(buffer, "Resume"); + s32 text_x = panel_x + (panel_w/2) - (renderer->calculate_text_width(fntb, buffer)/2); + s32 text_y = panel_y + (20*scale); + + color t_shadow = COLOR_TEXT_SHADOW; + t_shadow.a = opacity*255; + + renderer->render_text(fntb, text_x, text_y, buffer, t_shadow); + + text_x = panel_x + (20*scale); + text_y += fntb->px_h+(15*scale); + + sprintf(buffer, "Name: %s", resume->employee->name); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Age: %d", resume->employee->age); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Experience: %d years", resume->employee->experience); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Salary: $%.2f/month", resume->employee->salary); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + struct tm* curr_time = gmtime(&resume->expire_date); + text_y += fnt->px_h + 8*scale; + strftime(buffer, 50, "Offer open untill %d/%m/%Y", curr_time); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + text_y += fnt->px_h + 8*scale; + + s32 signature_h = ((panel_y+panel_h)-text_y)*0.6; + text_y += (10 * scale); + renderer->render_image(img_signature, text_x, text_y, signature_h*2, signature_h); + + if (!resume->animation.started) { + s32 button_w = 124 * scale; + s32 button_h = 37 * scale; + s32 pad = 20 * scale; + s32 btn_y = panel_y + panel_h + pad; + s32 btn_x = panel_x; + if (button_render(scale, true, "Hire", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + resume->animation.started = true; + resume->hired = true; + resume->employee->id = _active_world->next_id++; + resume->employee->hire_date = _active_world->current_time; + add_employee_to_world_location(_active_location, resume->employee); + } + btn_x = panel_x + panel_w - button_w; + if (button_render(scale, true, "Decline", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click3, AUDIO_CHANNEL_SFX_1); + resume->animation.started = true; + resume->hired = false; + } + } + else { + s32 pad = scale * 50; + s32 stamp_x = panel_x + pad; + s32 stamp_y = panel_y + pad; + s32 stamp_w = panel_w - (pad*2); + s32 stamp_h = panel_h - (pad*2); + if (resume->hired) { + renderer->render_image_tint(img_hired, stamp_x, stamp_y, stamp_w, stamp_h, color_bg); + } + else { + renderer->render_image_tint(img_denied, stamp_x, stamp_y, stamp_w, stamp_h, color_bg); + } + + animation_update(&resume->animation); + if (resume->animation.percentage == 1.0f) { + array_remove_at(&_active_location->resumes, 0); + } + } + } +} + +static void place_detail_draw_info(platform_window* window, tab tab) +{ + float pad = tab.scale * 10; + float x = tab.x+pad; + float y = tab.y+pad; + float w = tab.w-(pad*2); + float h = tab.h-(pad*2); + + renderer->render_set_scissor(window, x, y, w, h); + if (selected_tab_index == PLACE_DETAIL_EMPLOYEES) { + place_detail_draw_employees(window, tab, x, y, w, h); + } + else if (selected_tab_index == PLACE_DETAIL_JOBOFFERS) { + place_detail_draw_job_offers(window, tab, x, y, w, h); + } + else if (selected_tab_index == PLACE_DETAIL_GARAGE) { + place_detail_draw_trucks(window, tab, x, y, w, h); + } + + renderer->render_reset_scissor(window); +} + +static tab place_detail_draw_tab_bg(platform_window* window) +{ + float offset = scale * 40.0; + s32 cornor_size = img_button_topleft->width*(scale/2); + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)); + s32 w = (area.w * 0.9 - (offset*2)); + s32 top_width = w - (cornor_size*2); + s32 size_height = h - (cornor_size*2); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y + item_h; + + color fill = COLOR_BUTTON_ACTIVE_TINT; + + // button_render(scale, BUTTON_STATIC, 0, panel_x + pad_x, vertical_pad + panel_y + pad_y*2 + button_h*1, button_w, button_h); + + // left + renderer->render_image_tint(img_button_left, x, y-1, cornor_size, size_height+2, fill); + + // right + renderer->render_image_tint(img_button_right, x + cornor_size + top_width, y-1, cornor_size, size_height+2, fill); + + // bottom + renderer->render_image_tint(img_button_bottomleft, x, y + size_height, cornor_size, cornor_size, fill); + renderer->render_image_tint(img_button_bottom, x + cornor_size, y + size_height, top_width, cornor_size, fill); + renderer->render_image_tint(img_button_bottomright, x + cornor_size + top_width, y + size_height, cornor_size, cornor_size, fill); + + // fill + s32 pad = cornor_size-1; + fill = COLOR_BUTTON_ACTIVE; + renderer->render_rectangle(x+pad, y-1, w-(pad*2), h-(pad*2), fill); + + return (tab){x,y,w,h, scale}; +} + +static void place_detail_push_tab(place_detail_info_state index, platform_window* window, char* text) +{ + float offset = scale * 40.0; + s32 pos_y = area.y + (area.w*0.12); + s32 item_w = 426 * 0.4 * scale; + s32 item_h = 82 * 0.4 * scale; + s32 pos_x = offset + area.x + (area.w*0.05) + (item_w*index); + bool hovered = mouse_interacts(pos_x,pos_y,item_w,item_h); + + color tint = COLOR_WHITE; + if (selected_tab_index == index) { + tint = COLOR_BUTTON_ACTIVE_TINT; + } + if (hovered) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) { + if (selected_tab_index != index) audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + selected_tab_index = index; + } + } + + renderer->render_image_tint(img_tabitem, pos_x, pos_y, item_w, item_h, tint); + + { + font* fnt = fnt_rd24; + s32 text_x = pos_x + (item_w/2) - (renderer->calculate_text_width(fnt, text)/2); + s32 text_y = pos_y + (item_h/2) - (fnt->px_h/2); + + renderer->render_text(fnt, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, text, COLOR_TEXT); + } +} + +static tab place_detail_draw_tabs(platform_window* window) +{ + place_detail_push_tab(PLACE_DETAIL_EMPLOYEES, window, "Employees"); + place_detail_push_tab(PLACE_DETAIL_JOBOFFERS, window, "Job Offers"); + place_detail_push_tab(PLACE_DETAIL_SCHEDULE, window, "Schedule"); + place_detail_push_tab(PLACE_DETAIL_GARAGE, window, "Garage"); + return place_detail_draw_tab_bg(window); +} + +static void place_detail_draw_title(platform_window* window) +{ + char buf[200]; + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + strcpy(buf, _active_location->name); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_RESUMES) { + strcpy(buf, "Hire new employees"); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_DEALERS) { + strcpy(buf, "Dealers"); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + sprintf(buf, "%s, #%d", _active_employee->name, _active_employee->id); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE) { + if (_active_schedule_state == VIEWING) + sprintf(buf, "Schedule for %s", _active_location->name); + else { + sprintf(buf, "%s wants you to ship %s to %s", + _active_scheduling_job.offer.company->name, + _active_scheduling_job.offer.product->name, + (*(world_location**)array_at(&_active_scheduling_job.offer.connections, + _active_scheduling_job.offer.connections.length-1))->name); + } + } + + font* fnt = fnt_rd36; + float text_pad = scale * 40.0; + s32 text_x = text_pad + area.x + (area.w*0.05); + s32 text_y = text_pad + area.y + (area.w*0.05); + + // Title + { + renderer->render_text(fnt, text_x+2, text_y+2, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT); + } + + color tags_text_color = AN_LI_TINT(COLOR_TEXT, tag_animation); + color tag_icon_color = AN_LI_TINT(COLOR_WHITE, tag_animation); + + // Tags + #define PUSH_TAG(_text, _icon){\ + char* text = _text;\ + s32 textw = renderer->calculate_text_width(fnt, text);\ + text_x -= textw;\ + renderer->render_text(fnt, text_x, text_y, text, tags_text_color);\ + text_x -= icon_s + pad_between_icon_and_text;\ + renderer->render_image_tint(_icon, text_x, text_y,icon_s,icon_s, tag_icon_color);\ + text_x -= pad_between_tags;} + + text_y += (fnt->px_h/2); + fnt = fnt_rd20; + text_y -= (fnt->px_h/2); + s32 icon_s = fnt->px_h; + s32 pad_between_icon_and_text = 5*scale; + s32 pad_between_tags = 20*scale; + + #define ANIMATION_OFFSET (30*scale) + text_x = area.x + (area.w*0.05) + (area.w*0.9) - text_pad + (ANIMATION_OFFSET-(ANIMATION_OFFSET*tag_animation.percentage)); + #undef ANIMATION_OFFSET + + job_offer* job_to_inspect = 0; + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && _active_schedule_state != VIEWING) + job_to_inspect = &_active_scheduling_job.offer; + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && _active_schedule_state == VIEWING && _active_selected_scheduled_job != 0) + job_to_inspect = &_active_selected_scheduled_job->offer; + + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && job_to_inspect) { + // price + { + char pricebuf[25]; + sprintf(pricebuf, "$%d/trip", job_to_inspect->reward); + PUSH_TAG(pricebuf, img_coins); + } + + // distance + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fkm", job_to_inspect->total_distance); + PUSH_TAG(pricebuf, img_road); + } + + // duration + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fh-%.0fh", job_to_inspect->duration_sec_min/3600.0f, job_to_inspect->duration_sec_max/3600.0f); + PUSH_TAG(pricebuf, img_timer); + } + + if (_active_schedule_state == VIEWING) { + char namebuf[MAX_WORLD_LOCATION_NAME_LENGTH*3]; + job_endpoints endpoints = job_offer_get_endpoints(job_to_inspect); + sprintf(namebuf, "%s to %s", endpoints.source->name, endpoints.dest->name); + PUSH_TAG(namebuf, img_globe); + } + } + else if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + // Original location + { + char pricebuf[100]; + sprintf(pricebuf, "From: %s", get_world_location_by_id(_active_world, _active_employee->original_location_id)->name); + PUSH_TAG(pricebuf, img_city); + } + + // Current location or active trip destination + { + char pricebuf[100]; + if (_active_employee->current_location_id != INVALID_ID) { + sprintf(pricebuf, "Currently at: %s", get_world_location_by_id(_active_world, _active_employee->current_location_id)->name); + } + else if (_active_employee->active_job_id != INVALID_ID) { + active_job* j = get_active_job_by_id(_active_world, _active_employee->active_job_id); + job_endpoints endpoints = job_offer_get_endpoints(&j->offer); + sprintf(pricebuf, "Driving to: %s", endpoints.dest->name); + } + PUSH_TAG(pricebuf, img_location_pin); + } + } + + animation_update(&tag_animation); +} + +static void place_detail_draw_panel(platform_window* window) +{ + s32 panel_w = area.w*0.9; + s32 panel_h = area.h*0.7; + s32 panel_x = area.x + area.w*0.05; + s32 panel_y = area.y + area.w*0.05; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_w/10.0f); + s32 back_y = panel_y + panel_h - (back_h/2) - 1; + + if (push_back_button(scale, back_x, back_y, back_w, back_h)) { + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + else { + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + _goto_default_detail_state(); + } + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } + else if (is_left_clicked()) { + _goto_default_detail_state(); + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + game_set_active_scene(GAME_STATE_WORLD_MAP); + } +} + +void place_detail_scene_render(platform_window* window) +{ + renderer->set_render_depth(5); + world_map_draw_info_panel(window, false); + + renderer->set_render_depth(4); + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + tab t = place_detail_draw_tabs(window); + place_detail_draw_info(window, t); + + if (selected_tab_index == PLACE_DETAIL_SCHEDULE) { + selected_tab_index = PLACE_DETAIL_SHOW_MAIN; + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + } + } + if (current_detail_state == PLACE_DETAIL_SHOW_RESUMES) { + place_detail_draw_resumes(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_DEALERS) { + place_detail_draw_dealers(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + place_detail_draw_selected_employee(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE) { + place_detail_draw_schedule(window); + } + + renderer->set_render_depth(3); + place_detail_draw_title(window); + + renderer->set_render_depth(2); + place_detail_draw_panel(window); + + renderer->set_render_depth(1); + menu_draw_background(window); +} + +void place_detail_scene_update(platform_window* window) +{ + // world_update(window, _active_world, false); + + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + else { + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + _goto_default_detail_state(); + } +} + +void place_detail_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/save_state_select.c b/src/scenes/save_state_select.c new file mode 100644 index 0000000..1db5d11 --- /dev/null +++ b/src/scenes/save_state_select.c @@ -0,0 +1,105 @@ + +void save_state_select_scene_init() +{ + +} + +static bool push_save_state_button(float scale, bool enabled, s32 x, s32 y, s32 size) +{ + if (enabled) { + button_render(scale, true, 0, x, y, size, size); + } + else { + button_render(scale, true, 0, x, y, size, size); + float close_size = size/3; + float close_x = x + (size/2)-(close_size/2); + float close_y = y + (size/2)-(close_size/2); + renderer->render_image(img_close, close_x, close_y, close_size, close_size); + } + + return false; +} + +static bool push_back_button(float scale, s32 back_x, s32 back_y, s32 back_w, s32 back_h) +{ + bool result = false; + color tint = COLOR_WHITE; + if (mouse_interacts(back_x,back_y,back_w,back_h)) { + tint = COLOR_BUTTON_ACTIVE_TINT; + platform_set_cursor(main_window, CURSOR_POINTER); + if (is_left_clicked()) { + result = true; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + } + + renderer->render_image_tint(img_back, back_x, back_y, back_w, back_h, tint); + + font* font_sml = fnt_rd20; + char* back_text = "Back"; + s32 back_text_width = renderer->calculate_text_width(font_sml, back_text); + s32 text_x = back_x + (back_w/2) - (back_text_width/2) + (back_w/12); + s32 text_y = back_y + (back_h/2) - (font_sml->px_h/2); + + renderer->render_text(font_sml, text_x+2, text_y+2, back_text, COLOR_TEXT_SHADOW); + renderer->render_text(font_sml, text_x, text_y, back_text, COLOR_TEXT); + return result; +} + +static void save_state_draw_options(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + float vertical_pad = 20 * scale; + float horizontal_pad = vertical_pad; + float spacing = 5 * scale; + + s32 panel_h = 280 * scale; + s32 panel_item_size = (panel_h - (vertical_pad*2) - (spacing*1)) / 2; + s32 panel_w = (panel_item_size * 3) + (horizontal_pad*2) + (spacing*2); + + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // top row + for (s32 i = 0; i < 3; i++) + push_save_state_button(scale, 0, panel_x + horizontal_pad + (panel_item_size*i) + + (spacing*i), panel_y + horizontal_pad, panel_item_size); + + // bottom row + for (s32 i = 0; i < 3; i++) + push_save_state_button(scale, 0, panel_x + horizontal_pad + (panel_item_size*i) + (spacing*i), + panel_y + horizontal_pad + panel_item_size+spacing, panel_item_size); + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_item_size/3); + s32 back_y = panel_y + panel_h - (back_h/2) - 1; + + if (push_back_button(scale, back_x, back_y, back_w, back_h)) { + game_set_active_scene(GAME_STATE_MENU); + } + } +} + +void save_state_select_scene_render(platform_window* window) +{ + menu_draw_background(window); + save_state_draw_options(window); +} + +void save_state_select_scene_update(platform_window* window) +{ + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void save_state_select_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/settings_scene.c b/src/scenes/settings_scene.c new file mode 100644 index 0000000..23655d3 --- /dev/null +++ b/src/scenes/settings_scene.c @@ -0,0 +1,152 @@ +enum settings_state +{ + SETTINGS_AUDIO, + SETTINGS_DISPLAY, + SETTINGS_KEYBINDINGS, +}; + +enum settings_state settings_state = SETTINGS_AUDIO; + +void settings_scene_init() { + +} + +static void draw_display_settings(s32 x, s32 y, s32 w, s32 h) +{ + s32 option_spacing = 20*scale; + s32 checkbox_s = 30*scale; + s32 checkbox_offset = ((checkbox_s-fnt_rd24->px_h)/2); + s32 text_y = y + option_spacing; + s32 text_x = x + checkbox_s + option_spacing; + + #define PUSH_DISPLAY_OPTION(_str, _opt)\ + renderer->render_text(fnt_rd24, text_x, text_y, _str, COLOR_TEXT);\ + if (button_draw_background(scale, x,text_y-checkbox_offset, checkbox_s, checkbox_s, COLOR_WHITE, COLOR_BUTTON)) {\ + if (is_left_clicked()) { \ + _opt = !_opt;\ + if (&_opt == &option_vsync) platform_toggle_vsync(main_window, option_vsync);\ + if (&_opt == &option_fullscreen) platform_toggle_fullscreen(main_window, option_fullscreen);\ + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1);\ + }\ + }\ + if (_opt) {\ + s32 tw = renderer->calculate_text_width(fnt_rd24, "X");\ + renderer->render_text(fnt_rd24, x + checkbox_s/2 - tw/2,text_y-checkbox_offset + checkbox_s/2 - fnt_rd24->px_h/2, "X", COLOR_TEXT);\ + }\ + text_y += (checkbox_s + option_spacing); + + PUSH_DISPLAY_OPTION("Vsync", option_vsync); + PUSH_DISPLAY_OPTION("Fullscreen", option_fullscreen); +} + +static void draw_audio_settings(s32 x, s32 y, s32 w, s32 h) +{ + s32 option_spacing = 20*scale; + s32 slider_h = 30*scale; + s32 slider_offset = ((slider_h-fnt_rd24->px_h)/2); + s32 slider_w = w/2; + s32 slider_x = x + slider_w; + s32 text_y = y + option_spacing; + + // Music + #define PUSH_VOLUME_OPTION(_text, _opt)\ + {\ + static bool is_editing = false;\ + renderer->render_text(fnt_rd24, x, text_y, _text, COLOR_TEXT);\ + s32 slider_y = text_y - slider_offset;\ + float percentage = is_editing ? ((_global_mouse.x - (slider_x+10*scale)) / (float)(slider_w-20*scale)) : _opt;\ + if (percentage < 0.0f) percentage = 0.0f;\ + if (percentage > 1.0f) percentage = 1.0f;\ + button_draw_background_percentage(scale, slider_x,slider_y,slider_w,slider_h, COLOR_WHITE, COLOR_BUTTON, percentage, COLOR_WHITE);\ + bool hovered = mouse_interacts(slider_x,slider_y,slider_w,slider_h);\ + if (hovered && is_left_clicked()) is_editing = true;\ + if (is_editing) {\ + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_1, volume_sfx*volume_global);\ + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_2, volume_sfx*volume_global);\ + audio_set_music_volume(volume_music*volume_global);\ + }\ + if (is_editing) {\ + if (!is_left_down()) { is_editing = false; audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); }\ + _opt = percentage;\ + }\ + text_y += (slider_h + option_spacing);\ + } + + PUSH_VOLUME_OPTION("Global", volume_global); + PUSH_VOLUME_OPTION("Music", volume_music); + PUSH_VOLUME_OPTION("Interface", volume_sfx); +} + +static void settings_draw_options(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + s32 panel_w = 400 * scale; + s32 panel_h = 500 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // Buttons + s32 button_pad = 10*scale; + s32 button_w = (panel_w-(button_pad*4))/3; + s32 button_h = 37*scale; + s32 button_y = panel_y + button_pad*1.3f; + s32 button_x = panel_x + button_pad; + + if (button_render(scale, BUTTON_ENABLED, "Audio", button_x, button_y, button_w, button_h)) + { + settings_state = SETTINGS_AUDIO; + } + + if (button_render(scale, BUTTON_ENABLED, "Display", button_x + (button_w + button_pad), button_y, button_w, button_h)) + { + settings_state = SETTINGS_DISPLAY; + } + + if (button_render(scale, BUTTON_ENABLED, "Keybindings", button_x + (button_w + button_pad)*2, button_y, button_w, button_h)) + { + settings_state = SETTINGS_KEYBINDINGS; + } + + s32 detail_pad = 25*scale; + s32 detail_w = panel_w - (detail_pad*2)-(button_pad*2); + s32 detail_h = panel_h - (detail_pad*2)-(button_pad*3)-button_h; + s32 detail_x = panel_x+detail_pad+button_pad; + s32 detail_y = button_y + button_h + detail_pad+button_pad; + + if (settings_state == SETTINGS_AUDIO) { + draw_audio_settings(detail_x, detail_y, detail_w, detail_h); + } + else if (settings_state == SETTINGS_DISPLAY) { + draw_display_settings(detail_x, detail_y, detail_w, detail_h); + } + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_w/10); + s32 back_y = panel_y + panel_h - (back_h/2) - 1; + + if (push_back_button(scale, back_x, back_y, back_w, back_h)) { + game_set_active_scene(GAME_STATE_MENU); + } + } +} + +void settings_scene_render(platform_window* window) { + menu_draw_background(window); + settings_draw_options(window); +} + +void settings_scene_update(platform_window* window) { + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void settings_scene_destroy() { + +} \ No newline at end of file diff --git a/src/scenes/world_map.c b/src/scenes/world_map.c new file mode 100644 index 0000000..eb106cb --- /dev/null +++ b/src/scenes/world_map.c @@ -0,0 +1,1094 @@ +typedef enum t_world_map_scene_state +{ + WORLD_SCENE_STATE_IDLE, + WORLD_SCENE_STATE_PURCHASE_LOCATION, + WORLD_SCENE_STATE_LOG, + WORLD_SCENE_STATE_INSIGHTS, + WORLD_SCENE_STATE_INVEST, +} world_map_scene_state; + +typedef union t_world_map_scene_data +{ + world_location* location_to_purchase; +} world_map_scene_data; + +s32 insights_selected_year_index = 0; // years since first year. +world* _active_world = 0; +world_map_scene_state scene_state = WORLD_SCENE_STATE_IDLE; +world_map_scene_data scene_data = {0}; +active_job_ref currently_viewing_active_job = {0,0,0}; + +animation log_button_flash_animation = {0,0,0,1}; + +void place_detail_show_employee_detail(employee* emp); +void place_detail_show_schedule_with_highlighted_job(world_location* loc, scheduled_job* job, scheduled_job_time job_time); + +void world_map_set_active_world(world* world) +{ + _active_world = world; +} + +void world_map_scene_init() +{ + +} + +static bool push_info_panel_button(float scale, image* img, s32 x, s32 y, s32 size, bool enabled, bool flashing) +{ + button_type type = (enabled ? ((flashing && log_button_flash_animation.percentage >= 0.5f) ? BUTTON_HIGHLIGHTED : BUTTON_ENABLED) : BUTTON_DISABLED); + bool result = button_render(scale, type, 0, x, y, size, size); + + float img_size = size / 2; + renderer->render_image(img, x+(img_size/2), y + (img_size/2), img_size, img_size); + return result; +} + +static void world_map_draw_speed_panel(platform_window* window, bool enabled) +{ + s32 screen_center_x = area.x + (area.w/2); + + s32 panel_h = 60 * scale; + s32 panel_w = 180 * scale; + + float btn_size = 23 * scale; + float pad_top = 6 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = area.y + area.h - panel_h - (100 * scale); + s32 text_y = panel_y + pad_top; + float side_btn_offset = pad_top; + + // Background panel + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // Button decrease simulation speed + if (push_info_panel_button(scale, img_arrow_left, screen_center_x - (panel_w/2) + side_btn_offset, text_y, btn_size, enabled, false)) { + _active_world->simulation_speed/=2; + } + // Button increase simulation speed + if (push_info_panel_button(scale, img_arrow_right, screen_center_x + (panel_w/2) - btn_size - side_btn_offset, text_y, btn_size, enabled, false)) { + _active_world->simulation_speed*=2; + if (_active_world->simulation_speed == 0) _active_world->simulation_speed = 1; + } + + // Validate new simulation speed + if (_active_world->simulation_speed < MIN_SIMULATION_SPEED) _active_world->simulation_speed = MIN_SIMULATION_SPEED; + if (_active_world->simulation_speed > MAX_SIMULATION_SPEED) _active_world->simulation_speed = MAX_SIMULATION_SPEED; + + // Draw the current speed, or pause icon when paused + if (_active_world->simulation_speed != 0) { + font* fnt = fnt_rd20; + + char buf[10]; + sprintf(buf, "%dx", _active_world->simulation_speed); + + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = panel_x + (panel_w/2)-(tw/2); + renderer->render_text(fnt, textx, text_y+(5*scale), buf, COLOR_TEXT); + } + else { + s32 icon_pad = 4*scale; + s32 icon_s = btn_size-(icon_pad*2); + renderer->render_image(img_pause, panel_x+(panel_w/2)-(icon_s/2), text_y+icon_pad, icon_s, icon_s); + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } +} + +static void world_map_draw_info_panel(platform_window* window, bool enabled) +{ + world_map_draw_speed_panel(window, enabled); + + s32 screen_center_x = area.x + (area.w/2); + + float vertical_pad = 20 * scale; + + s32 panel_h = 120 * scale; + s32 panel_w = 280 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = area.y + area.h - panel_h - (10 * scale); + + // Draw background panel + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + if (enabled) { + char txt_buf[50]; + char* text = txt_buf; + s32 text_y; + s32 text_x; + font* font_big = fnt_rd32; + s32 game_title_width; + + // Draw current date and time + { + strftime(txt_buf, 50, "%H:%M %d/%m/%Y", &_active_world->current_time); + game_title_width = renderer->calculate_text_width(font_big, text); + text_y = panel_y + vertical_pad; + text_x = screen_center_x - (game_title_width/2); + + renderer->render_text(font_big, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_big, text_x, text_y, text, COLOR_TEXT); + } + + // Draw money + { + sprintf(txt_buf, "$%.0f", _active_world->money); + text = txt_buf; + font* font_medium = fnt_rd24; + game_title_width = renderer->calculate_text_width(font_medium, text); + text_y = text_y + font_big->px_h + (10 * scale); + text_x = screen_center_x - (game_title_width/2); + + renderer->render_text(font_medium, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_medium, text_x, text_y, text, COLOR_TEXT); + } + + float btn_size = 35 * scale; + float btn_spacing = 2 * scale; + float btn_y = panel_y + panel_h - btn_size - vertical_pad/2; + float total_btn_w = (btn_size+btn_spacing)*4-btn_spacing; + + if (log_button_flash_animation.percentage == 1.0f) log_button_flash_animation = animation_create(1000); + log_button_flash_animation.started = true; + animation_update(&log_button_flash_animation); + + // Graph button + if (push_info_panel_button(scale, img_graph, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*0, btn_y, btn_size, enabled, false)) { + scene_state = (scene_state == WORLD_SCENE_STATE_INSIGHTS) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_INSIGHTS; + } + + // Event log button + if (push_info_panel_button(scale, img_list, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*1, btn_y, btn_size, enabled, _active_world->log.has_unread_messages)) { + scene_state = (scene_state == WORLD_SCENE_STATE_LOG) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_LOG; + } + + // Bank button + push_info_panel_button(scale, img_bank, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*2, btn_y, btn_size, enabled, false); + + // Event log button + if (push_info_panel_button(scale, img_list, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*3, btn_y, btn_size, enabled, _active_world->log.has_unread_messages)) { + scene_state = (scene_state == WORLD_SCENE_STATE_INVEST) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_INVEST; + } + } + else { + s32 icon_s = panel_h/2; + renderer->render_image(img_pause, panel_x+(panel_w/2)-(icon_s/2), panel_y+(panel_h/2)-(icon_s/2), icon_s, icon_s); + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } +} + +static void _insights_draw_grid(float scale, bool invalid_location, money_data_collection* collection, s32 grid_rows, s32 grid_cols, s32 textpad, s32 x, s32 y, s32 w, s32 h, s32 width_per_item, s32 height_per_item) +{ + font* fnt = fnt_rd16; + font* fnt_title = fnt_rd24; + + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + if (!collection) break; + money_data data = collection->months[i]; + if (isnan(data.total_income)) continue; + + char textbuf[50]; + #define PUSH_VAL(_index, _val, _neg)\ + sprintf(textbuf, "$%.0f", fabs(_val));\ + renderer->render_text(fnt, x + ((i+3)*width_per_item) + (textpad/2), y + (_index*height_per_item)+(height_per_item/2)-(fnt->px_h/2), textbuf, (_neg) ? COLOR_TEXT_NEGATIVE : COLOR_TEXT); + + PUSH_VAL(1, data.income_from_trips, false); + PUSH_VAL(2, data.expenses_from_utility, true); + PUSH_VAL(3, data.expenses_from_healthcare, true); + PUSH_VAL(4, data.expenses_from_repairs, true); + PUSH_VAL(5, data.expenses_from_fuel, true); + PUSH_VAL(6, data.expenses_from_employees, true); + PUSH_VAL(7, data.expenses_from_trucks, true); + // ADD NEW ENTRY HERE + PUSH_VAL(9, data.total_income, false); + PUSH_VAL(10, data.total_expenses, true); + PUSH_VAL(11, data.total_profit, data.total_profit < 0.0f); + } + + renderer->render_rectangle(x, y, width_per_item*3, h, COLOR_SCHEDULE_BG); + renderer->render_rectangle(x, y, w, height_per_item, COLOR_SCHEDULE_BG); + + for (s32 tx = 3; tx < grid_cols; tx++) { + s32 posx = x + tx * width_per_item; + renderer->render_rectangle(posx,y,1,h, COLOR_SCHEDULE_BORDER_THIN); + + char buf[50]; + buf[0] = 0; + + switch(tx-2) { + case 1: strcpy(buf, "Jan"); break; + case 2: strcpy(buf, "Feb"); break; + case 3: strcpy(buf, "Mar"); break; + case 4: strcpy(buf, "Apr"); break; + case 5: strcpy(buf, "May"); break; + case 6: strcpy(buf, "Jun"); break; + case 7: strcpy(buf, "Jul"); break; + case 8: strcpy(buf, "Aug"); break; + case 9: strcpy(buf, "Sep"); break; + case 10: strcpy(buf, "Oct"); break; + case 11: strcpy(buf, "Nov"); break; + case 12: strcpy(buf, "Dec"); break; + default: break; + } + + s32 textw = renderer->calculate_text_width(fnt_title, buf); + renderer->render_text(fnt_title, posx + (width_per_item/2)-(textw/2), y+(height_per_item/2)-(fnt_title->px_h/2), buf, COLOR_TEXT); + } + + for (s32 ty = 0; ty < grid_rows; ty++) { + s32 posy = y + ty * height_per_item; + renderer->render_rectangle(x,posy,w,1, COLOR_SCHEDULE_BORDER_THIN); + + char buf[50]; + buf[0] = 0; + + switch(ty) { + case 1: strcpy(buf, "Income"); break; + case 2: strcpy(buf, "Utility"); break; + case 3: strcpy(buf, "Employee Benefits"); break; + case 4: strcpy(buf, "Repairs"); break; + case 5: strcpy(buf, "Fuel"); break; + case 6: strcpy(buf, "Salaries"); break; + case 7: strcpy(buf, "Trucks"); break; + // ADD NEW ENTRY HERE + case 9: strcpy(buf, "Total Income"); break; + case 10: strcpy(buf, "Total Expenses"); break; + case 11: strcpy(buf, "Total Profit"); break; + default: break; + } + + renderer->render_text(fnt_title, x + textpad, posy+(height_per_item/2)-(fnt_title->px_h/2), buf, COLOR_TEXT); + } + + if (!collection) { + char* txtbuf = invalid_location ? "Invalid location" : "No data for time period"; + s32 textw = renderer->calculate_text_width(fnt_title, txtbuf); + s32 textx = x + (width_per_item*9) - (textw/2); + s32 texty = y + (h/2)-(fnt_title->px_h/2); + renderer->render_text(fnt_title, textx, texty, txtbuf, COLOR_TEXT); + } + + renderer->render_rectangle_outline(x,y,w,h, 1, COLOR_SCHEDULE_BORDER); +} + +static void _insights_draw_chart(platform_window*window, bool invalid_location, float scale, money_data_collection* collection, s32 grid_rows, s32 grid_cols, s32 textpad, s32 x, s32 y, s32 w, s32 h, s32 width_per_item, s32 height_per_item) +{ + font* fnt = fnt_rd16; + font* fnt_big = fnt_rd24; + + s32 xaxis_height = fnt_big->px_h; + h -= xaxis_height; + + s32 max_val = 0; + s32 min_val = INT_MAX; + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + if (!collection) break; + money_data data = collection->months[i]; + + #define CHECK_MIN_MAX(_val)\ + if (_val > max_val) max_val = _val;\ + if (_val < min_val) min_val = _val; + + CHECK_MIN_MAX(data.income_from_trips); + CHECK_MIN_MAX(data.expenses_from_utility); + CHECK_MIN_MAX(data.expenses_from_healthcare); + CHECK_MIN_MAX(data.expenses_from_repairs); + CHECK_MIN_MAX(data.expenses_from_fuel); + CHECK_MIN_MAX(data.expenses_from_employees); + CHECK_MIN_MAX(data.expenses_from_trucks); + // ADD NEW ENTRY HERE + CHECK_MIN_MAX(data.total_income); + CHECK_MIN_MAX(data.total_expenses); + CHECK_MIN_MAX(data.total_profit); + } + + if (min_val == INT_MAX) min_val = 0; + + s32 steps = 10; + s32 step_size = 0; + + // Round min and max to nearest 10k + { + s32 max = max_val >= abs(min_val) ? max_val : abs(min_val); + s32 round_to = 10000; + if (max > 10000) round_to = 10000; + if (max > 100000) round_to = 100000; + if (max > 1000000) round_to = 1000000; + if (max > 10000000) round_to = 10000000; + if (max > 100000000) round_to = 100000000; + + max_val = max_val + round_to - max_val % round_to; + if (min_val > round_to) min_val = min_val - min_val % round_to; + else min_val = min_val - round_to - (min_val%round_to); + + // make sure negative and positive y-axis is symmetrical + if (max_val > abs(min_val)) { + min_val = -max_val; + } + else if (max_val < abs(min_val)) { + max_val = abs(min_val); + } + + step_size = (max_val-min_val)/steps; + } + + static bool enabled_categories[10] = {1,1,1,1,1,1,1,1,1,1}; + + color colors[] = { + rgb(87, 82, 208), + rgb(3, 118, 247), + rgb(54, 166, 214), + rgb(50, 140, 250), + rgb(90, 196, 248), + rgb(78, 213, 95), + rgb(252, 200, 3), + rgb(249, 146, 5), + rgb(249, 59, 47), + rgb(249, 45, 82), + }; + + char* legenda_items[] = { + "Income", + "Utility", + "Employee Benefits", + "Repairs", + "Fuel", + "Salaries", + "Trucks", + // ADD NEW ENTRY HERE + "Total Income", + "Total Expenses", + "Total Profit", + }; + + // Legenda + { + s32 text_spacing = 10*scale; + s32 outline_spacing = text_spacing/2; + s32 _index = 0; + + #define PUSH_LEGENDA_ITEM(_text){\ + s32 yy = y+((fnt_big->px_h+text_spacing)*_index);\ + s32 totalw = width_per_item*3;\ + bool hovered = (_global_mouse.x >= x-outline_spacing && _global_mouse.x <= x+totalw+outline_spacing\ + && _global_mouse.y >= yy-outline_spacing && _global_mouse.y <= yy + fnt_big->px_h+outline_spacing);\ + if (hovered) {platform_set_cursor(window, CURSOR_POINTER); if (is_left_clicked())\ + { enabled_categories[_index] = !enabled_categories[_index]; audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); }}\ + renderer->render_rectangle(x,yy,fnt_big->px_h, fnt_big->px_h, enabled_categories[_index] ? colors[_index] : LEGENDA_COLOR_DISABLED);\ + if (hovered) renderer->render_rectangle(x-outline_spacing,yy-outline_spacing,totalw+(text_spacing),fnt_big->px_h+(text_spacing), LEGENDA_HOVER_BACKGROUND_COLOR);\ + renderer->render_text(fnt_big,x+fnt_big->px_h+text_spacing,yy,_text,COLOR_TEXT);_index++;} + + for (s32 i = 0; i < sizeof(legenda_items)/sizeof(char*); i++) { + PUSH_LEGENDA_ITEM(legenda_items[i]); + } + } + + // Graph y axis + { + s32 linex = x + 4*width_per_item; + + float step_h = (h)/(float)steps; + for (s32 i = 0; i < steps+1; i++) { + char buf[20]; + s32 val = max_val - (step_size*i); + sprintf(buf, "%d", val); + s32 textw = renderer->calculate_text_width(fnt, buf); + renderer->render_text(fnt, x+(width_per_item*4)-textw-(10*scale), y+(step_h*i)-(fnt->px_h/2), buf, COLOR_TEXT); + renderer->render_rectangle(linex, y+h*(i/(float)steps), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_SUB_COLOR_DISABLED); + } + + { + renderer->render_rectangle(linex, y + h - (h*1.0f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + renderer->render_rectangle(linex, y + h - (h*0.5f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + renderer->render_rectangle(linex, y + h - (h*0.0f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + } + } + + // Graph x axis + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + char buf[50]; + buf[0] = 0; + + switch(i+1) { + case 1: strcpy(buf, "Jan"); break; + case 2: strcpy(buf, "Feb"); break; + case 3: strcpy(buf, "Mar"); break; + case 4: strcpy(buf, "Apr"); break; + case 5: strcpy(buf, "May"); break; + case 6: strcpy(buf, "Jun"); break; + case 7: strcpy(buf, "Jul"); break; + case 8: strcpy(buf, "Aug"); break; + case 9: strcpy(buf, "Sep"); break; + case 10: strcpy(buf, "Oct"); break; + case 11: strcpy(buf, "Nov"); break; + case 12: strcpy(buf, "Dec"); break; + default: break; + } + s32 textw = renderer->calculate_text_width(fnt,buf); + + renderer->render_text(fnt, x+(width_per_item*(i+4))-(textw/2), y+h+(10*scale), buf, COLOR_TEXT); + } + + // Graph data + { + s32 total_diff = max_val-min_val; + if (total_diff == 0) total_diff = 1; + + s32 dot_size = 4*scale; + s32 dot_offset =dot_size/2; + s32 _index = 0; + //if (min_val < 0) min_val = 0; + + #define DRAW_LINES_FOR_DATA(_var){\ + s32 last_dot_x = 0;\ + s32 last_dot_y = 0;\ + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) {\ + if (!collection) break;\ + if (!enabled_categories[_index]) break;\ + money_data data = collection->months[i];\ + if (isnan(data.total_income)) continue;\ + s32 val_diff = (_var) - min_val;\ + s32 dot_x = x + (i+4)*width_per_item;\ + s32 dot_y = y + h - (h*(val_diff/(float)total_diff));\ + renderer->render_image_tint(img_dot, dot_x-dot_offset, dot_y-dot_offset, dot_size, dot_size, colors[_index]);\ + if (mouse_interacts(dot_x-dot_offset, dot_y-dot_offset, dot_size, dot_size)) {\ + reset_left_click();\ + s32 info_x = dot_x;\ + s32 info_y = dot_y+(dotsize/2);\ + char info_txt[50];\ + sprintf(info_txt, "%s: $%.0f", legenda_items[_index], _var);\ + show_tooltip(info_x, info_y, info_txt);\ + }\ + if (last_dot_x != 0) {\ + renderer->render_line(dot_x, dot_y, last_dot_x, last_dot_y, 1, colors[_index]);\ + }\ + last_dot_x = dot_x;\ + last_dot_y = dot_y;\ + }_index++;\ + } + + DRAW_LINES_FOR_DATA(data.income_from_trips); + DRAW_LINES_FOR_DATA(data.expenses_from_utility); + DRAW_LINES_FOR_DATA(data.expenses_from_healthcare); + DRAW_LINES_FOR_DATA(data.expenses_from_repairs); + DRAW_LINES_FOR_DATA(data.expenses_from_fuel); + DRAW_LINES_FOR_DATA(data.expenses_from_employees); + DRAW_LINES_FOR_DATA(data.expenses_from_trucks); + // ADD NEW ENTRY HERE + DRAW_LINES_FOR_DATA(data.total_income); + DRAW_LINES_FOR_DATA(data.total_expenses); + DRAW_LINES_FOR_DATA(data.total_profit); + } + + if (!collection) { + char* txtbuf = invalid_location ? "Invalid location" : "No data for time period"; + s32 textw = renderer->calculate_text_width(fnt_big, txtbuf); + s32 textx = x + (width_per_item*9) - (textw/2); + s32 texty = y + (h/2)-(fnt_big->px_h/2); + renderer->render_text(fnt_big, textx, texty, txtbuf, COLOR_TEXT); + } +} + +static void world_map_draw_insights(platform_window* window) +{ + s32 w = area.w*0.9; + s32 h = area.h*0.7; + s32 x = area.x + area.w*0.05; + s32 y = area.y + area.w*0.05; + s32 w_orig = w; + s32 h_orig = h; + s32 x_orig = x; + s32 y_orig = y; + panel_render(scale, x, y, w, h); + + #define GRID_ROWS ((sizeof(money_data)/sizeof(float))+2) + #define GRID_COLS (MONTHS_IN_YEAR+3) + + s32 button_row_h = 44*scale; + s32 btn_size = 34*scale; + s32 spacing = 5*scale; + + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 textpad = 20*scale; + + w -= pad*2; + h -= pad*2; + + h -= button_row_h+spacing; + + s32 width_per_item = (w/GRID_COLS); + s32 height_per_item = (h/GRID_ROWS); + s32 new_w = width_per_item*GRID_COLS; + s32 new_h = height_per_item*GRID_ROWS; + s32 off_x = w - new_w; + s32 off_y = h - new_h; + w = new_w; + h = new_h; + + s32 panel_w = w+(halfpad*2); + + enum insights_panel_format + { + CHART, + GRID, + }; + static enum insights_panel_format current_format = GRID; + static world_location* active_world_location_filter = 0; + + { + s32 btn_row_x = x + off_x/2+halfpad; + s32 btn_row_y = y + off_y/2+halfpad; + + s32 button_pad = (button_row_h - btn_size)/2; + s32 btn_start_x = btn_row_x + panel_w - (4*scale); + s32 btn_left_start_x = btn_row_x+(4*scale)+button_pad; + s32 year_w = (80*scale); + s32 year_text_w = year_w+(button_pad*2); + s32 btn_y = btn_row_y + button_pad; + + // background + button_render(scale, BUTTON_STATIC, 0, btn_row_x, btn_row_y,panel_w, button_row_h); + + // Buttons right + if (push_info_panel_button(scale, img_graph, btn_start_x - ((btn_size + button_pad)*1), btn_y, btn_size, current_format!=CHART, false)) (current_format = CHART); + if (push_info_panel_button(scale, img_grid, btn_start_x - ((btn_size + button_pad)*2), btn_y, btn_size, current_format!=GRID, false)) (current_format = GRID); + + font* fnt = fnt_rd24; + s32 current_year = 1900+_active_world->start_year+insights_selected_year_index; + + // Buttons left + button_render(scale, BUTTON_STATIC, 0, btn_left_start_x+btn_size+button_pad, btn_y,year_w, btn_size); + char buf[10]; + sprintf(buf, "%d", current_year); + s32 textw = renderer->calculate_text_width(fnt,buf); + renderer->render_text(fnt,btn_left_start_x+(btn_size)+(year_text_w/2)-(textw/2), btn_y+(btn_size/2)-(fnt->px_h/2), buf, COLOR_TEXT); + + s32 btn_right_x = btn_left_start_x + (btn_size) + year_text_w; + if (push_info_panel_button(scale, img_arrow_left, btn_left_start_x, btn_y, btn_size, insights_selected_year_index>=1, false)) (insights_selected_year_index--); + if (push_info_panel_button(scale, img_arrow_right, btn_right_x, btn_y, + btn_size, insights_selected_year_index<_active_world->insights.length-1, false)) (insights_selected_year_index++); + + // Location selector + s32 tb_filter_x = btn_right_x + btn_size + button_pad; + s32 tb_width = 520*scale; + active_world_location_filter = location_selector_render(window, scale, true, active_world_location_filter, tb_filter_x, btn_y, tb_width, btn_size); + + // Clear button + s32 clear_btn_x = tb_filter_x + tb_width + button_pad; + if (push_info_panel_button(scale, img_globe, clear_btn_x, btn_y, btn_size, _global_keyboard.input_text_len, false)) { + active_world_location_filter = 0; + keyboard_set_input_text(""); + } + } + + x+=off_x/2+pad; + y+=off_y/2+pad+button_row_h+spacing; + + button_render(scale, BUTTON_STATIC, 0, x-halfpad,y-halfpad,panel_w,h+(halfpad*2)); + + static money_data_collection* prev_collection = 0; + money_data_collection* collection = get_current_insights_data(_active_world); // Get current year page, also a hack to make sure a new page exists at year switch. + if (!prev_collection || collection != prev_collection) { + prev_collection = collection; + insights_selected_year_index = _active_world->insights.length-1; + } + + money_data_collection* collection_to_use = array_at(&_active_world->insights, insights_selected_year_index); // Get page to use + if (active_world_location_filter) { + if (active_world_location_filter->is_owned) { + s32 index_of_filter_page = insights_selected_year_index; + s32 purchase_year_diff = active_world_location_filter->purchase_year - _active_world->start_year; + index_of_filter_page -= purchase_year_diff; + if (index_of_filter_page < 0) collection_to_use = 0; + else if (index_of_filter_page > active_world_location_filter->insights.length-1) collection_to_use = 0; + else collection_to_use = array_at(&active_world_location_filter->insights, index_of_filter_page); + } + else { + collection_to_use = 0; + } + } + + bool is_typing_in_filter_tb = (!active_world_location_filter && _global_keyboard.input_text_len); + if (is_typing_in_filter_tb) collection_to_use = 0; + + if (current_format == GRID) _insights_draw_grid(scale, is_typing_in_filter_tb, collection_to_use, GRID_ROWS, GRID_COLS, textpad, x, y, w, h, width_per_item, height_per_item); + if (current_format == CHART) _insights_draw_chart(window, is_typing_in_filter_tb, scale, collection_to_use, GRID_ROWS, GRID_COLS, textpad, x, y, w, h, width_per_item, height_per_item); + + if (mouse_interacts(x_orig, y_orig, w_orig, h_orig)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_event_log(platform_window* window) +{ + _active_world->log.has_unread_messages = false; + + font* fnt = fnt_rd16; + + s32 panel_h = area.h*0.9f; + s32 panel_pad = area.h*0.05f; + + s32 panel_w = area.w/3.5f; + + s32 panel_x = area.x + panel_pad; + s32 panel_y = area.y + panel_pad; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + float text_pad = panel_w*0.05f; + + s32 text_y = panel_y + text_pad; + s32 text_x = panel_x + text_pad; + s32 text_w = panel_w - (text_pad*2); + + renderer->render_set_scissor(window, text_x, text_y, panel_w, panel_h-(text_pad*2)); + + if (_active_world->log.events.length) { + s32 read_cursor = _active_world->log.write_cursor; + for (s32 i = 0; i < _active_world->log.events.length; i++) { + read_cursor--; + if (read_cursor < 0) read_cursor = _active_world->log.events.length-1; + + event* e = array_at(&_active_world->log.events, read_cursor); + + float inner_pad = 5*scale; + + + s32 texth = renderer->render_text_cutoff(fnt, text_x+inner_pad, text_y+inner_pad, e->message, COLOR_TEXT, text_w-(inner_pad*2)); + s32 highlight_w = text_w; + s32 highlight_h = texth+(inner_pad); + + if (mouse_interacts(text_x, text_y, highlight_w, highlight_h)) { + platform_set_cursor(window, CURSOR_POINTER); + renderer->render_rectangle(text_x, text_y, text_w, highlight_h, rgba(255,255,255,20)); + + if (is_left_clicked()) { + switch (e->type) + { + case EVENT_TYPE_MISSED_SHIPMENT_NO_TRUCK: + place_detail_show_employee_detail((employee*)e->data); + break; + case EVENT_TYPE_MISSED_SHIPMENT_NO_ASSIGNEE: + case EVENT_TYPE_MISSED_SHIPMENT_NOT_AT_LOCATION: { + scheduled_job* job = (scheduled_job*)e->data; + place_detail_show_schedule_with_highlighted_job(job->location, job, e->job_time); + } break; + case EVENT_TYPE_EMPLOYEE_QUIT: + place_detail_show_schedule_with_highlighted_job((world_location*)e->data, 0, e->job_time); + break; + + default: + log_assert(0, "Invalid event type."); + break; + } + } + } + + text_y += highlight_h; + if (text_y > panel_y + panel_h) break; + } + } + else { + char* buf = "No events."; + s32 tw = renderer->calculate_text_width(fnt, buf); + renderer->render_text_cutoff(fnt, text_x + (text_w/2)-(tw/2), text_y, "No events.", COLOR_TEXT, text_w); + } + + renderer->render_reset_scissor(window); + + if (mouse_interacts(panel_x, panel_y, panel_w, panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +s32 hovered_investment_panel_item_index = -1; +static s32 world_map_push_invest_panel_item(s32 x, s32 y, s32 w, s32 index, char* text, s32* fval) +{ + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 panel_w = w-(pad); + float item_pad = 30*scale; + float item_spacing = 5*scale; + float item_halfpad = item_pad/2; + + font* fnt = fnt_rd24; + char value_str[30]; + sprintf(value_str, "$%d", *fval); + float item_h = fnt->px_h+item_pad; + float item_x = x+halfpad; + float item_y = y+halfpad+(index*(item_h+item_spacing)); + float item_w = panel_w; + float content_x = item_x+item_halfpad; + float content_y = item_y+item_halfpad; + float total_val_editor_w = item_w*0.3f; + float button_left_x = item_x + item_w - total_val_editor_w - item_halfpad; + float btn_size = fnt->px_h*2; + float btn_y = item_y + ((item_h-btn_size)/2); + button_render(scale, BUTTON_STATIC, 0, item_x, item_y,item_w,item_h); + renderer->render_text(fnt, content_x, content_y, text, COLOR_TEXT); + // Button decrease simulation speed + if (push_info_panel_button(scale, img_arrow_left, button_left_x, btn_y, btn_size, *fval > 0, false)) { + *fval -= 100.0f; + } + // Button increase simulation speed + if (push_info_panel_button(scale, img_arrow_right, button_left_x+total_val_editor_w-btn_size, btn_y, btn_size, true, false)) { + *fval += 100.0f; + } + s32 item_text_w = renderer->calculate_text_width(fnt, value_str); + s32 item_text_x = button_left_x + (total_val_editor_w/2) - (item_text_w/2); + renderer->render_text(fnt, item_text_x, content_y, value_str, COLOR_TEXT); + + if (mouse_interacts_peak(item_x, item_y, item_w, item_h)) { + hovered_investment_panel_item_index = index; + } + + return item_y + item_h; +} + +static void world_map_draw_invest_panel(platform_window* window) +{ + s32 w = area.w*0.5; + s32 h = area.h*0.7; + s32 x = area.x + area.w*0.25; + s32 y = area.y + area.w*0.05; + panel_render(scale, x, y, w, h); + + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 panel_w = w-(pad); + + #define TOTAL_INVEST_ITEM_COUNT (5) + + hovered_investment_panel_item_index = -1; + + world_map_push_invest_panel_item(x, y, w, 0, "Workspace safety", (s32*)(&_active_world->investments.safety)); + world_map_push_invest_panel_item(x, y, w, 1, "Marketing", (s32*)(&_active_world->investments.marketing)); + world_map_push_invest_panel_item(x, y, w, 2, "Human resources", (s32*)(&_active_world->investments.human_resources)); + world_map_push_invest_panel_item(x, y, w, 3, "Employee training", (s32*)(&_active_world->investments.training)); + s32 item_bottom = world_map_push_invest_panel_item(x, y, w, 4, "Legal department", (s32*)(&_active_world->investments.legal)); + + float detail_panel_y = item_bottom + halfpad; + float detail_panel_h = h - (detail_panel_y-y)-halfpad; + + button_render(scale, BUTTON_STATIC, 0, x+halfpad, detail_panel_y,panel_w,detail_panel_h); + + s32 info_text_pad = 20*scale; + + char* info_text = 0; + switch (hovered_investment_panel_item_index) + { + case 0: info_text = "Increasing the budget for workspace safety will ensure employees receive the proper workspace safety training."; + break; + case 1: info_text = "Increasing the marketing budget will give your business more publicity and will secure more work proposals."; + break; + case 2: info_text = "Investing in human resources will guarantee more responses to vacancies."; + break; + case 3: info_text = "Providing training for your employees will increase happiness for your employees and make them work more efficiently."; + break; + case 4: info_text = "The legal department is essential for settling legal disputes."; + break; + } + + if (info_text) + renderer->render_text_cutoff(fnt_rd24, x+halfpad+info_text_pad, detail_panel_y+info_text_pad, info_text, COLOR_TEXT, panel_w - (info_text_pad*2)); + + if (mouse_interacts(x,y,w,h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_purchase_location_panel(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + float vertical_pad = 20 * scale; + + s32 panel_h = 160 * scale; + s32 panel_w = 280 * scale; + + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // info text + { + font* font_title = FONT_REGULAR(SIZE_RD(area.w, 32)); + { + char* title = scene_data.location_to_purchase->name; + s32 text_w = renderer->calculate_text_width(font_title, title); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + (vertical_pad); + renderer->render_text(font_title, text_x+2, text_y+2, title, COLOR_TEXT_SHADOW); + renderer->render_text(font_title, text_x, text_y, title, COLOR_TEXT); + } + { + char buf[100]; + sprintf(buf, "Purchase a garage for $%.0f?", world_location_get_price(scene_data.location_to_purchase)); + char* text = buf; + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 text_w = renderer->calculate_text_width(font_info, text); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + vertical_pad*1.5+font_title->px_h; + renderer->render_text(font_info, text_x+2, text_y+2, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_info, text_x, text_y, text, COLOR_TEXT); + } + } + + s32 button_w = 178 * scale; + s32 button_h = 37 * scale; + if (button_render(scale, true, "Purchase", screen_center_x - (button_w/2), panel_y + panel_h - button_h - vertical_pad, button_w, button_h)) + { + // Mark as owned. + scene_data.location_to_purchase->is_owned = true; + scene_data.location_to_purchase->purchase_year = _active_world->current_time.tm_year; + + // Make sure insights are ready to display. + money_data_collection* collection = get_current_insights_data_for_location(_active_world, scene_data.location_to_purchase); + collection->months[_active_world->current_time.tm_mon].total_income = 0; + + _active_world->money -= world_location_get_price(scene_data.location_to_purchase); + + scene_state = WORLD_SCENE_STATE_IDLE; + } + + button_w = 30 * scale; + if (button_render(scale, true, "X", panel_x+panel_w-button_w, panel_y+2, button_w, button_w)) + { + scene_state = WORLD_SCENE_STATE_IDLE; + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_viewing_job(platform_window* window) +{ + active_job* job = get_active_job_by_ref(_active_world, currently_viewing_active_job); + if (!job) { + currently_viewing_active_job.offerid = INVALID_ID; + return; + } + + s32 panel_offset_from_job = area.h*0.1f; + s32 panel_h = area.h*0.32f; + s32 panel_w = area.w*0.15f; + + s32 panel_x = job->px_pos.x; + s32 panel_y = job->px_pos.y; + + if (job->px_pos.y > area.y + (area.h/2)) { + panel_y -= panel_h - panel_offset_from_job; + + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3), panel_y+panel_h-5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3*2), panel_y+panel_h-5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + } + else { + panel_y += panel_offset_from_job; + + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3), panel_y+5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3*2), panel_y+5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + } + + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + s32 panel_pad = 10*scale; + s32 truck_img_x = panel_x + panel_pad; + s32 truck_img_y = panel_y + panel_pad; + s32 truck_img_s = panel_w * 0.6f; + + s32 portrait_s = truck_img_s / 2; + + renderer->render_image(job->assigned_truck.logo, truck_img_x+(panel_pad), truck_img_y, truck_img_s, truck_img_s); + draw_employee_portrait(&job->assignee, truck_img_x+(panel_pad) + truck_img_s - (portrait_s/3), truck_img_y + (truck_img_s / 3), portrait_s, portrait_s); + + { + s32 text_x = truck_img_x; + float text_pad = (5*scale); + s32 text_y = truck_img_y + truck_img_s + panel_pad; + font* fnt = fnt_rd16; + font* fnt_s = fnt_rd12; + renderer->render_text(fnt,text_x,text_y,job->assignee.name,COLOR_TEXT); + text_y += fnt->px_h + text_pad; + + // Name + origin + { + world_location* origin = get_world_location_by_id(_active_world, job->assignee.original_location_id); + char buf[100]; + sprintf(buf, "Works at %s", origin->name); + renderer->render_text(fnt_s,text_x,text_y,buf,COLOR_TEXT); + text_y += fnt_s->px_h + text_pad; + } + + // Job info + { + text_y += (20*scale); + + char daybuf[10]; + switch(job->day) { + case 1: strcpy(daybuf, "Mon"); break; + case 2: strcpy(daybuf, "Tue"); break; + case 3: strcpy(daybuf, "Wed"); break; + case 4: strcpy(daybuf, "Thu"); break; + case 5: strcpy(daybuf, "Fri"); break; + case 6: strcpy(daybuf, "Sat"); break; + case 0: strcpy(daybuf, "Sun"); break; + } + + char buf[100]; + sprintf(buf, "Left on %s %02d:%02d", daybuf, WORK_HOUR_START + (job->timeslot/TIME_SLOTS_PER_HOUR), job->timeslot%TIME_SLOTS_PER_HOUR); + renderer->render_text(fnt_s,text_x,text_y,buf,COLOR_TEXT); + text_y += fnt_s->px_h + text_pad; + } + + // Job status + if (!job->reversed) + { + job_endpoints endpoints = job_offer_get_endpoints(&job->offer); + char buf[100]; + sprintf(buf, "Shipping %s to %s", job->offer.product->name, endpoints.dest->name); + text_y += renderer->render_text_cutoff(fnt_s,text_x,text_y,buf,COLOR_TEXT, panel_w - (panel_pad*2)); + text_y += text_pad; + } + else { + world_location* source = *(world_location**)array_at(&job->offer.connections, 0); // Get source location + char buf[100]; + sprintf(buf, "Returning to %s", source->name); + renderer->render_text_cutoff(fnt_s,text_x,text_y,buf,COLOR_TEXT, panel_w - (panel_pad*2)); + text_y += fnt_s->px_h + text_pad; + } + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + currently_viewing_active_job.offerid = INVALID_ID; + } +} + +static void world_handle_scroll(platform_window* window) +{ + static float target_zoom = 1.0f; + if (global_ui_context.mouse->scroll_state == SCROLL_UP) + { + if (target_zoom < zoom) target_zoom = zoom; + target_zoom+=0.1f; + } + if (global_ui_context.mouse->scroll_state == SCROLL_DOWN) + { + if (target_zoom > zoom) target_zoom = zoom; + target_zoom-=0.1f; + } + + // Keep camera position + if (target_zoom != zoom) { + vec4 area = camera_get_target_rectangle(window); + int orig_w = area.w * zoom; + int orig_h = area.h * zoom; + int new_w = area.w * target_zoom; + int new_h = area.h * target_zoom; + + float errorw = new_w - orig_w; + float errorh = new_h - orig_h; + camera_x -= errorw / 25.0f; + camera_y -= errorh / 40.0f; + } + + // Smooth scrolling + if (target_zoom != zoom) + { + float error = target_zoom - zoom; + zoom += error / 10; + } + + if (zoom < 1.0f) zoom = 1.0f; + if (zoom > 5.0f) zoom = 5.0f; + + if (camera_x > 0.0f) camera_x = 0.0f; + if (camera_y > 0.0f) camera_y = 0.0f; + + //if (is_left_down_peak()) + { + + } +} + +void world_map_scene_render(platform_window* window) +{ + renderer->set_render_depth(5); + + world_handle_scroll(window); + + world_map_draw_info_panel(window, true); + + renderer->set_render_depth(4); + switch (scene_state) + { + case WORLD_SCENE_STATE_INSIGHTS: + world_map_draw_insights(window); + break; + case WORLD_SCENE_STATE_LOG: + world_map_draw_event_log(window); + break; + case WORLD_SCENE_STATE_IDLE: break; + case WORLD_SCENE_STATE_PURCHASE_LOCATION: + world_map_draw_purchase_location_panel(window); + break; + case WORLD_SCENE_STATE_INVEST: + world_map_draw_invest_panel(window); + break; + } + + renderer->set_render_depth(3); + if (currently_viewing_active_job.offerid != INVALID_ID) world_map_draw_viewing_job(window); + + if (_active_world) { + world_update_result click_result = world_render(window, _active_world); + + if (click_result.clicked_location) { + if (click_result.clicked_location->is_owned) { + place_detail_set_active_location(click_result.clicked_location); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + } + else { + scene_data.location_to_purchase = click_result.clicked_location; + scene_state = WORLD_SCENE_STATE_PURCHASE_LOCATION; + } + } + else if (click_result.clicked_job) { + currently_viewing_active_job = (active_job_ref){click_result.clicked_job->day, click_result.clicked_job->timeslot, click_result.clicked_job->offer.id}; + } + } + + renderer->set_render_depth(0); + + vec4 area = camera_get_target_rectangle(window); + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WORLD_MAP_BACKGROUND); + renderer->render_image(img_world_map, area.x + camera_x, area.y + camera_y, area.w*zoom, area.h*zoom); +} + +void world_map_scene_update(platform_window* window) +{ + world_update(window, _active_world); + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +void world_map_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/tooltip.c b/src/tooltip.c new file mode 100644 index 0000000..5098ba7 --- /dev/null +++ b/src/tooltip.c @@ -0,0 +1,22 @@ +#define TOOLTIP_PAD (scale*14) + +void show_tooltip(s32 x, s32 y, char* buf) { + tooltop_visible = true; + string_copyn(tooltip_buffer, buf, 100); + tooltip_x = x+(20*scale); + tooltip_y = y-(fnt_rd16->px_h + (TOOLTIP_PAD))/2; +} + +void update_render_tooltip() { + if (!tooltop_visible) return; + s32 current_render_depth = gl_render_depth; + renderer->set_render_depth(10); + + s32 info_w = renderer->calculate_text_width(fnt_rd16, tooltip_buffer) + (TOOLTIP_PAD); + s32 info_h = fnt_rd16->px_h + (TOOLTIP_PAD); + button_draw_background(scale, tooltip_x, tooltip_y, info_w, info_h, COLOR_WHITE, COLOR_BUTTON); + renderer->render_text(fnt_rd16, tooltip_x+(TOOLTIP_PAD/2), tooltip_y+(TOOLTIP_PAD/2), tooltip_buffer, COLOR_TEXT); + + renderer->set_render_depth(current_render_depth); + tooltop_visible = false; +} diff --git a/src/ui/animation.c b/src/ui/animation.c new file mode 100644 index 0000000..62775bf --- /dev/null +++ b/src/ui/animation.c @@ -0,0 +1,18 @@ +animation animation_create(s32 duration) +{ + animation an; + an.time = 0; + an.started = false; + an.duration = duration; + an.percentage = 0.0f; + return an; +} + +float animation_update(animation* an) +{ + if (!an->started) return an->percentage; + an->time += frame_delta*1000.0f; + if (an->time > an->duration) an->time = an->duration; + an->percentage = an->time/an->duration; + return an->percentage; +} \ No newline at end of file diff --git a/src/ui/button.c b/src/ui/button.c new file mode 100644 index 0000000..773fb7b --- /dev/null +++ b/src/ui/button.c @@ -0,0 +1,101 @@ +bool button_draw_background_percentage(float scale, s32 x, s32 y, s32 w, s32 h, color tint, color fill, float percentage, color bar_fill) +{ + s32 cornor_size = img_button_topleft->width*(scale/2); + s32 top_width = w - (cornor_size*2); + s32 size_height = h - (cornor_size*2); + + // top + renderer->render_image_tint(img_button_topleft, x, y, cornor_size, cornor_size, tint); + renderer->render_image_tint(img_button_top, x + cornor_size, y, top_width, cornor_size, tint); + renderer->render_image_tint(img_button_topright, x + cornor_size + top_width, y, cornor_size, cornor_size, tint); + + // left + renderer->render_image_tint(img_button_left, x, y + cornor_size-1, cornor_size, size_height+2, tint); + + // right + renderer->render_image_tint(img_button_right, x + cornor_size + top_width, y + cornor_size-1, cornor_size, size_height+2, tint); + + // bottom + renderer->render_image_tint(img_button_bottomleft, x, y + cornor_size + size_height, cornor_size, cornor_size, tint); + renderer->render_image_tint(img_button_bottom, x + cornor_size, y + cornor_size + size_height, top_width, cornor_size, tint); + renderer->render_image_tint(img_button_bottomright, x + cornor_size + top_width, y + cornor_size + size_height, cornor_size, cornor_size, tint); + + // fill + s32 pad = cornor_size-1; + renderer->render_rectangle(x+pad, y+pad, w-(pad*2), h-(pad*2), fill); + renderer->render_rectangle(x+pad, y+pad, (w-(pad*2))*percentage, h-(pad*2), bar_fill); + + return _global_mouse.x >= x && _global_mouse.x <= x + w && _global_mouse.y >= y && _global_mouse.y <= y + h; +} + +bool button_draw_background(float scale, s32 x, s32 y, s32 w, s32 h, color tint, color fill) +{ + s32 cornor_size = img_button_topleft->width*(scale/2); + s32 top_width = w - (cornor_size*2); + s32 size_height = h - (cornor_size*2); + + // top + renderer->render_image_tint(img_button_topleft, x, y, cornor_size, cornor_size, tint); + renderer->render_image_tint(img_button_top, x + cornor_size, y, top_width, cornor_size, tint); + renderer->render_image_tint(img_button_topright, x + cornor_size + top_width, y, cornor_size, cornor_size, tint); + + // left + renderer->render_image_tint(img_button_left, x, y + cornor_size-1, cornor_size, size_height+2, tint); + + // right + renderer->render_image_tint(img_button_right, x + cornor_size + top_width, y + cornor_size-1, cornor_size, size_height+2, tint); + + // bottom + renderer->render_image_tint(img_button_bottomleft, x, y + cornor_size + size_height, cornor_size, cornor_size, tint); + renderer->render_image_tint(img_button_bottom, x + cornor_size, y + cornor_size + size_height, top_width, cornor_size, tint); + renderer->render_image_tint(img_button_bottomright, x + cornor_size + top_width, y + cornor_size + size_height, cornor_size, cornor_size, tint); + + // fill + s32 pad = cornor_size-1; + renderer->render_rectangle(x+pad, y+pad, w-(pad*2), h-(pad*2), fill); + + return _global_mouse.x >= x && _global_mouse.x <= x + w && _global_mouse.y >= y && _global_mouse.y <= y + h; +} + +bool button_render(float scale, button_type enabled, char* text, s32 x, s32 y, s32 w, s32 h) +{ + bool result = false; + + color tint = COLOR_WHITE; + color fill = COLOR_BUTTON; + + if (enabled == BUTTON_ENABLED || enabled == BUTTON_HIGHLIGHTED) { + if (mouse_interacts(x,y,w,h)) { + platform_set_cursor(main_window, CURSOR_POINTER); + if (is_left_clicked()) { + result = true; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + tint = COLOR_BUTTON_ACTIVE_TINT; + fill = COLOR_BUTTON_ACTIVE; + } + + if (enabled == BUTTON_HIGHLIGHTED) { + tint = COLOR_BUTTON_HIGHLIGHTED_TINT; + fill = COLOR_BUTTON_HIGHLIGHTED_ACTIVE; + } + } + else if (enabled == BUTTON_DISABLED) { + tint = COLOR_BUTTON_DISABLED_TINT; + fill = COLOR_BUTTON_DISABLED; + } + + button_draw_background(scale,x,y,w,h,tint,fill); + + // text + if (text) { + font* font_sml = fnt_rd24; + s32 text_y = y + (h/2) - (font_sml->px_h/2); + s32 game_title_width = renderer->calculate_text_width(font_sml, text); + s32 text_x = x + (w/2) - (game_title_width/2); + + renderer->render_text(font_sml, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_sml, text_x, text_y, text, COLOR_TEXT); + } + return result; +} \ No newline at end of file diff --git a/src/ui/panel.c b/src/ui/panel.c new file mode 100644 index 0000000..af24e66 --- /dev/null +++ b/src/ui/panel.c @@ -0,0 +1,29 @@ + +void panel_render(float scale, s32 x, s32 y, s32 w, s32 h) +{ + s32 cornor_size = img_panel_topleft->width*(scale/2); + log_assert(w > cornor_size*2, "Panel width too small"); + log_assert(h > cornor_size*2, "Panel height too small"); + s32 top_width = w - (cornor_size*2); + s32 size_height = h - (cornor_size*2); + + // top + renderer->render_image(img_panel_topleft, x, y, cornor_size, cornor_size); + renderer->render_image(img_panel_top, x + cornor_size, y, top_width, cornor_size); + renderer->render_image(img_panel_topright, x + cornor_size + top_width, y, cornor_size, cornor_size); + + // left + renderer->render_image(img_panel_left, x, y + cornor_size-1, cornor_size, size_height+2); + + // right + renderer->render_image(img_panel_right, x + cornor_size + top_width, y + cornor_size-1, cornor_size, size_height+2); + + // bottom + renderer->render_image(img_panel_bottomleft, x, y + cornor_size + size_height, cornor_size, cornor_size); + renderer->render_image(img_panel_bottom, x + cornor_size, y + cornor_size + size_height, top_width, cornor_size); + renderer->render_image(img_panel_bottomright, x + cornor_size + top_width, y + cornor_size + size_height, cornor_size, cornor_size); + + // fill + s32 pad = cornor_size-1; + renderer->render_rectangle(x+pad, y+pad, w-(pad*2), h-(pad*2), COLOR_PANEL_BACKGROUND); +} \ No newline at end of file diff --git a/src/ui/portrait.c b/src/ui/portrait.c new file mode 100644 index 0000000..09301fe --- /dev/null +++ b/src/ui/portrait.c @@ -0,0 +1,21 @@ +void draw_employee_portrait(employee* emp, float x, float y, float w, float h) +{ + float body_s = h*0.8f; + float body_x = x + (w/2)-(body_s/2); + float body_y = y+h-body_s; + + float head_s = h*0.45f; + float head_x = x + (w/2)-(head_s/2); + float head_y = y+(h*0.11f); + + float hair_s = h*0.6f; + float hair_x = x + (w/2)-(hair_s/2); + float hair_y = y; + + //renderer->render_image(img_portrait, x,y,w,h); + + renderer->render_image_tint(img_portrait_head, head_x, head_y, head_s, head_s, emp->face_color); + renderer->render_image_tint(img_portrait_body, body_x, body_y, body_s, body_s, emp->body_color); + renderer->render_image_tint(img_portrait_hair[emp->portrait_hair_type], hair_x, hair_y, hair_s, hair_s, emp->hair_color); + +} \ No newline at end of file diff --git a/src/ui/selectors.c b/src/ui/selectors.c new file mode 100644 index 0000000..dc5a6bc --- /dev/null +++ b/src/ui/selectors.c @@ -0,0 +1,212 @@ + +employee* employee_selector_render(platform_window* window, float scale, bool enabled, employee* current_val, s32 x, s32 y, s32 w, s32 h, animation an, scheduled_job* offer) { + #define ANIMATION_OFFSET (-30*scale) + color text_color = AN_LI_TINT(COLOR_TEXT, an); + color c_tb_tint = AN_LI_TINT(COLOR_TEXTBOX_TINT, an); + color c_tb_fill = AN_LI_TINT(COLOR_TEXTBOX_FILL, an); + color c_white = AN_LI_TINT(COLOR_WHITE, an); + color c_correct = AN_LI_TINT(COLOR_CORRECT, an); + color c_wrong = AN_LI_TINT(COLOR_WRONG, an); + color c_btn = AN_LI_TINT(COLOR_BUTTON, an); + + employee* result = current_val; + + s32 tb_pad = 20*scale; + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + + s32 tb_width = 220*scale; + s32 tb_height = h-(tb_pad*2); + s32 tb_y = y+tb_pad; + s32 tb_x = x+tb_pad + (ANIMATION_OFFSET - (ANIMATION_OFFSET*an.percentage)); + + { + // Start taking input on click. + if (button_draw_background(scale, tb_x,tb_y,tb_width,tb_height, c_tb_tint, c_tb_fill) && enabled) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) { + _global_keyboard.take_input = true; + _global_keyboard.input_mode = INPUT_NUMERIC; + } + } + // Clear on outside click or enter. + else if (is_left_clicked_peak()) { + _global_keyboard.take_input = false; + } + if (keyboard_is_key_pressed(KEY_ENTER)) { + _global_keyboard.take_input = false; + } + + bool is_editing = _global_keyboard.take_input; + + // Limit input length + if (is_editing) { + if (_global_keyboard.input_text_len > MAX_INPUT_LENGTH_FOR_EMPLOYEE_SELECTOR) + { + _global_keyboard.input_text_len = MAX_INPUT_LENGTH_FOR_EMPLOYEE_SELECTOR; + _global_keyboard.cursor = MAX_INPUT_LENGTH_FOR_EMPLOYEE_SELECTOR; + _global_keyboard.input_text[MAX_INPUT_LENGTH_FOR_EMPLOYEE_SELECTOR] = 0; + } + + { + // Activation underline. + s32 linew = tb_width*0.95f; + s32 offsetw = (tb_width - linew)/2; + renderer->render_rectangle(tb_x+offsetw, tb_y+tb_height - 6, linew, 2, COLOR_SELECTOR_UNDERLINE); + } + } + + font* fnt_big = fnt_rd32; + s32 textx = tb_x+(10*scale); + s32 texty = tb_y+(tb_height/2)-(fnt_big->px_h/2); + + u32 text_id = string_to_u32(_global_keyboard.input_text); + employee* emp = is_editing ? get_global_employee_by_id(_active_world, text_id) : current_val; + + if (is_editing || emp || _global_keyboard.input_text_len) // Editing, employee is selected, or player is typing. + { + s32 cursorh = tb_height*0.6f; + + // Either display keyboard input or selected employee. + char idbuf[20]; + if (is_editing || !emp) sprintf(idbuf, "ID: %s", _global_keyboard.input_text); + else if (emp) sprintf(idbuf, "ID: %d", emp->id); + + s32 textw = renderer->render_text(fnt_big, textx, texty, idbuf, text_color); + if (is_editing) renderer->render_rectangle(textx+textw, tb_y+(tb_height/2)-(cursorh/2), 3, cursorh, text_color); + result = emp; + } + else if (!is_editing && !emp) { // Not editing and no employee selected. + renderer->render_text(fnt_big, textx, texty, "Search by ID..", text_color); + } + + s32 status_s = tb_height/2; + s32 status_x = tb_x + tb_width - (status_s/4*3); + s32 status_y = tb_y - (status_s/4); + button_draw_background(scale,status_x,status_y,status_s,status_s,c_white,c_btn); + s32 status_icon_s = status_s*0.5f; + s32 status_icon_offset = (status_s - status_icon_s)/2; + renderer->render_image_tint(result ? img_checkmark : img_questionmark, + status_x+status_icon_offset, status_y+status_icon_offset, status_icon_s, status_icon_s, result ? c_correct : c_wrong); + } + + // Draw employee data + if (result) { + s32 por_x = tb_x + tb_width + tb_pad; + draw_employee_portrait(result, por_x, tb_y, tb_height, tb_height); + + s32 text_x = por_x + tb_height + tb_pad; + font* fnt = fnt_rd24; + font* fnt_s = fnt_rd20; + renderer->render_text(fnt,text_x,tb_y,result->name,text_color); + + tb_y += fnt->px_h + (5*scale); + + { + world_location* origin = get_world_location_by_id(_active_world, result->original_location_id); + char buf[100]; + sprintf(buf, "Works at %s", origin->name); + renderer->render_text(fnt_s,text_x,tb_y,buf,text_color); + tb_y += fnt->px_h + (12*scale); + } + + float hours_for_this_job = (offer->offer.duration_sec_min * get_shiptime_factor(result)) / 3600.0f; + float total_hours = get_worked_hours_per_week_for_employee(_active_world, result, (_active_schedule_state == RESCHEDULING_JOB ? _active_selected_scheduled_job : 0)); + + // If being scheduled, also add timeslots that are currently being scheduled. + if (enabled) { + for (s32 i = 0; i < MAX_SHIPDAYS; i++) { + scheduled_job_time slot = offer->timeslots[i]; + if (slot.assignee == result) { + total_hours += hours_for_this_job; + if (!slot.stay_at_destination) total_hours += hours_for_this_job; + } + } + } + bool overworked = (total_hours > MAX_WORKED_HOURS_WEEKLY); + total_hours = ceil(total_hours); + + color c = COLOR_TEXT; + if (overworked) c = COLOR_TEXT_NEGATIVE; + char txt_status[50]; + sprintf(txt_status, "Currently scheduled for %.0fh/week", total_hours); + renderer->render_text(fnt_s,text_x,tb_y,txt_status,c); + } + + return result; + #undef ANIMATION_OFFSET +} + +world_location* location_selector_render(platform_window* window, float scale, bool enabled, world_location* current_val, s32 x, s32 y, s32 w, s32 h) +{ + world_location* result = current_val; + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + + s32 tb_width = w; + s32 tb_height = h; + s32 tb_y = y; + s32 tb_x = x; + + { + // Start taking input on click. + if (button_draw_background(scale, tb_x,tb_y,tb_width,tb_height, COLOR_TEXTBOX_TINT, COLOR_TEXTBOX_FILL) && enabled) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) { + _global_keyboard.take_input = true; + _global_keyboard.input_mode = INPUT_FULL; + } + } + // Clear on outside click or enter. + else if (is_left_clicked_peak()) { + _global_keyboard.take_input = false; + } + if (keyboard_is_key_pressed(KEY_ENTER)) { + _global_keyboard.take_input = false; + } + + bool is_editing = _global_keyboard.take_input; + + // Limit input length + if (is_editing) { + if (_global_keyboard.input_text_len > MAX_WORLD_LOCATION_NAME_LENGTH) + { + _global_keyboard.input_text_len = MAX_WORLD_LOCATION_NAME_LENGTH; + _global_keyboard.cursor = MAX_WORLD_LOCATION_NAME_LENGTH; + _global_keyboard.input_text[MAX_WORLD_LOCATION_NAME_LENGTH] = 0; + } + } + + font* fnt_big = fnt_rd32; + s32 textx = tb_x+(10*scale); + s32 texty = tb_y+(tb_height/2)-(fnt_big->px_h/2); + + world_location* emp = is_editing ? get_world_location_by_name(_active_world, _global_keyboard.input_text) : current_val; + + if (is_editing || emp || _global_keyboard.input_text_len) // Editing, employee is selected, or player is typing. + { + s32 cursorh = tb_height*0.6f; + + // Either display keyboard input or selected employee. + char idbuf[50]; + if (is_editing || !emp) sprintf(idbuf, "Name: %s", _global_keyboard.input_text); + else if (emp) sprintf(idbuf, "Name: %s", emp->name); + + s32 textw = renderer->render_text(fnt_big, textx, texty, idbuf, COLOR_TEXT); + if (is_editing) renderer->render_rectangle(textx+textw, tb_y+(tb_height/2)-(cursorh/2), 3, cursorh, COLOR_TEXT); + result = emp; + } + else if (!is_editing && !emp) { // Not editing and no employee selected. + renderer->render_text(fnt_big, textx, texty, "Filter location..", COLOR_TEXT); + } + + s32 status_s = tb_height/2; + s32 status_x = tb_x + tb_width - (status_s/4*3); + s32 status_y = tb_y - (status_s/4); + button_draw_background(scale,status_x,status_y,status_s,status_s,COLOR_WHITE,COLOR_BUTTON); + s32 status_icon_s = status_s*0.5f; + s32 status_icon_offset = (status_s - status_icon_s)/2; + renderer->render_image_tint(result ? img_checkmark : img_questionmark, + status_x+status_icon_offset, status_y+status_icon_offset, status_icon_s, status_icon_s, result ? COLOR_CORRECT : COLOR_WRONG); + } + + return result; +} \ No newline at end of file diff --git a/src/world.c b/src/world.c new file mode 100644 index 0000000..fbb2ec2 --- /dev/null +++ b/src/world.c @@ -0,0 +1,1321 @@ +static void world_assign_new_job_offers(world* world); +static double distance_between_location(world_location* location1, world_location* location2); +static void world_payout_salaries(world* world); +static void enable_insights_for_current_month(world* world); +static vec2f get_world_location_for_job(platform_window* window, world* world, active_job* job); +static employee* get_employee_by_id(world_location* location, u32 id); +static void end_contract_with_employee(world* world, employee* emp); + +float dotsize = 5; + +static s32 get_random_number(s32 min, s32 max) +{ + log_assert(min < max, "Min cannot be larger than max"); + // it is assumed srand has been initialized at world load. + return min + rand() % (max-min); +} + +void world_report_event_ex(world* world, char* msg, event_type type, void* data, scheduled_job_time scheduled_time) +{ + event new_event; + new_event.data = data; + new_event.type = type; + new_event.job_time = scheduled_time; + + char txt_buf[50]; + struct tm* time = gmtime(&world->simulation_time); + strftime(txt_buf, 50, "%H:%M %d/%m/%Y", time); + + snprintf(new_event.message, MAX_EVENT_MESSAGE_LENGTH, "[%s] %s", txt_buf, msg); + + if (world->log.events.length < LOG_HISTORY_LENGTH) { + array_push(&world->log.events, &new_event); + world->log.write_cursor++; + world->log.has_unread_messages = true; + } + else { + if (world->log.write_cursor >= LOG_HISTORY_LENGTH) world->log.write_cursor = 0; + u16 write_location = world->log.write_cursor; + world->log.write_cursor++; + + event* e = array_at(&world->log.events, write_location); + *e = new_event; + world->log.has_unread_messages = true; + } + + audio_play_sound(snd_event, AUDIO_CHANNEL_SFX_2); +} + +void world_report_event(world* world, char* msg, event_type type, void* data) +{ + world_report_event_ex(world, msg, type, data, (scheduled_job_time){0,0,0,0}); +} + +static job_endpoints job_offer_get_endpoints(job_offer* offer) +{ + world_location* source = *(world_location**)array_at(&offer->connections, 0); + world_location* dest = *(world_location**)array_at(&offer->connections, offer->connections.length-1); + + return (job_endpoints){source,dest}; +} + +static s32 employee_calculate_happiness_stars(employee* emp) +{ + s32 stars = emp->happiness / 0.2f; + if (stars > 5) stars = 0; + return stars; +} + +static world_location world_create_location(u8 size, double latitude, double longitude, char* name, bool is_owned) +{ + world_location location; + location.size = size; + location.latitude = latitude; + location.longitude = longitude; + string_copyn(location.name, name, MAX_WORLD_LOCATION_NAME_LENGTH); + location.is_owned = is_owned; + location.connections = array_create(sizeof(world_location*)); + + location.employees = array_create(sizeof(employee*)); + array_reserve(&location.employees, MAX_EMPLOYEE_COUNT); + + location.job_offers = array_create(sizeof(job_offer)); + array_reserve(&location.job_offers, MAX_JOBOFFER_COUNT); + + location.trucks = array_create(sizeof(truck)); + array_reserve(&location.trucks, MAX_TRUCK_COUNT); + + location.schedule.jobs = array_create(sizeof(scheduled_job)); + array_reserve(&location.schedule.jobs, TIME_SLOTS_PER_DAY*NUM_DAYS); + + location.insights = array_create(sizeof(money_data_collection)); + + location.resumes = array_create(sizeof(resume)); + location.id = assets_hash_path(location.name); + location.reliability = 1.0f; + + return location; +} + +static void world_create_connections(world* world) +{ + const double max_distance = 2.5; // about 155km + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* source = array_at(&world->locations, i); + for (s32 d = 0; d < world->locations.length; d++) + { + world_location* destination = array_at(&world->locations, d); + if (destination == source) continue; + + if (fabs(source->latitude - destination->latitude) < max_distance || + fabs(source->longitude - destination->longitude) < max_distance) { + array_push(&source->connections, &destination); + } + } + } +} + +world_location* get_world_location_by_id(world* world, s32 id) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* source = array_at(&world->locations, i); + if (source->id == id) return source; + } + return 0; +} + +world_location* get_world_location_by_name(world* world, char* str) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* source = array_at(&world->locations, i); + if (strcmp(source->name, str) == 0) return source; + } + return 0; +} + +void add_truck_to_world_location(world* world, world_location* location, truck* tr) +{ + log_assert(location->trucks.length < MAX_TRUCK_COUNT, "Too many trucks"); + tr->id = world->next_id++; + array_push(&location->trucks, tr); +} + +void add_employee_to_world_location(world_location* location, employee* employee) +{ + log_assert(location->employees.length < MAX_EMPLOYEE_COUNT, "Too many employees"); + employee->current_location_id = location->id; + array_push(&location->employees, &employee); +} + +static employee* create_employee(world* world, world_location* hired_at) +{ + employee* employee1 = mem_alloc(sizeof(employee)); + char* firstname = array_at(&world->firstnames, get_random_number(0, world->firstnames.length)); + char* lastname = array_at(&world->lastnames, get_random_number(0, world->lastnames.length)); + + snprintf(employee1->name, MAX_EMPLOYEE_NAME_LENGTH, "%s %s", firstname, lastname); + employee1->age = get_random_number(18, 60); + employee1->hire_date = world->current_time; + employee1->days_below_happiness_treshold = 0; + employee1->experience = (u8)((employee1->age - 18) * (get_random_number(1, 100)/100.0f)); + employee1->salary = BASE_PAY + (employee1->experience * RAISE_PER_YEAR); + if (employee1->salary > MAX_PAY) employee1->salary = MAX_PAY; + employee1->happiness = 1.0f; + employee1->original_location_id = hired_at->id; + employee1->current_location_id = INVALID_ID; + employee1->assigned_truck = 0; + employee1->active_job_id = INVALID_ID; + + // Portrait generation. + { + employee1->portrait_hair_type = get_random_number(0, PORTRAIT_MAX_HAIR_COUNT); + employee1->hair_color = hair_palette[get_random_number(0, (sizeof(hair_palette)/sizeof(color)))]; + employee1->face_color = skin_palette[get_random_number(0, (sizeof(skin_palette)/sizeof(color)))]; + employee1->body_color = body_palette[get_random_number(0, (sizeof(body_palette)/sizeof(color)))]; + } + + return employee1; +} + +static bool world_create_default_state(world* world) +{ + world_location* start_location = get_world_location_by_name(world, "Maastricht"); + if (!start_location) return false; + start_location->is_owned = true; + start_location->purchase_year = world->current_time.tm_year; + + employee* employee1 = create_employee(world, start_location); + employee1->id = world->next_id++; + add_employee_to_world_location(start_location, employee1); + + employee* employee2 = create_employee(world, start_location); + employee2->id = world->next_id++; + add_employee_to_world_location(start_location, employee2); + + employee* employee3 = create_employee(world, start_location); + employee3->id = world->next_id++; + add_employee_to_world_location(start_location, employee3); + + truck_dealer* dealer = array_at(&world->truck_dealers, 0); + truck* tr = array_at(&dealer->trucks, 0); + add_truck_to_world_location(world, start_location, tr); + add_truck_to_world_location(world, start_location, tr); + add_truck_to_world_location(world, start_location, tr); + + { + truck* t1 = array_at(&start_location->trucks, 0); + truck* t2 = array_at(&start_location->trucks, 1); + t1->assigned_employee = employee1; + employee1->assigned_truck = t1; + t2->assigned_employee = employee2; + employee2->assigned_truck = t2; + } + + return true; +} + +static bool world_load_boat_routes_from_file(world* world) +{ + world->boat_routes = array_create(sizeof(boat_route)); + file_content locations_file = platform_read_file_content("data/world/boat-routes.json", "rb"); + if (locations_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(locations_file.content); + if (!json_object) return false; + + cJSON *route; + cJSON_ArrayForEach(route, json_object) + { + boat_route new_route; + memset(&new_route, 0, sizeof(new_route)); + + boat_route_point new_point = (boat_route_point){0.0f,0.0f}; + s32 index = 0; + + cJSON *route_point; + cJSON_ArrayForEach(route_point, route) + { + if (index % 2 == 0) { + new_point.x = route_point->valuedouble; + } + else { + new_point.y = route_point->valuedouble; + new_route.points[new_route.count++] = new_point; + } + + index++; + } + + array_push(&world->boat_routes, &new_route); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&locations_file); + + return true; +} + +static bool world_load_companies_from_file(world* world) +{ + world->companies = array_create(sizeof(company)); + file_content locations_file = platform_read_file_content("data/world/companies.json", "rb"); + if (locations_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(locations_file.content); + if (!json_object) return false; + + cJSON *country_entry; + cJSON_ArrayForEach(country_entry, json_object) + { + company new_company; + new_company.products = array_create(sizeof(product)); + + cJSON* name = cJSON_GetObjectItem(country_entry, "name"); + if (!name) return false; + + cJSON* logo = cJSON_GetObjectItem(country_entry, "logo"); + if (!logo) return false; + + string_copyn(new_company.name, name->valuestring, MAX_PRODUCT_NAME_LENGTH); + new_company.logo = assets_find_image_ref(0, assets_hash_path(logo->valuestring)); + if (!new_company.logo) return false; + + cJSON* products = cJSON_GetObjectItem(country_entry, "products"); + if (!products) return false; + + cJSON *product_entry; + cJSON_ArrayForEach(product_entry, products) + { + char tmp_buf[MAX_PRODUCT_NAME_LENGTH]; + string_copyn(tmp_buf, product_entry->valuestring, MAX_PRODUCT_NAME_LENGTH); + array_push(&new_company.products, tmp_buf); + } + + array_push(&world->companies, &new_company); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&locations_file); + + return true; +} + +static bool world_load_lastnames_from_file(world* world) +{ + world->lastnames = array_create(MAX_ENPOLYEE_LASTNAME_LENGTH); + array_reserve(&world->lastnames, 5000); + + file_content firstname_file = platform_read_file_content("data/world/last-names.json", "rb"); + if (firstname_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(firstname_file.content); + if (!json_object) return false; + + cJSON *name_entry; + cJSON_ArrayForEach(name_entry, json_object) + { + char buffer[MAX_ENPOLYEE_LASTNAME_LENGTH]; + string_copyn(buffer, name_entry->valuestring, MAX_ENPOLYEE_LASTNAME_LENGTH); + array_push(&world->lastnames, buffer); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&firstname_file); + + world_create_connections(world); + + return true; +} + +static bool world_load_trucks_from_file(world* world) +{ + world->truck_dealers = array_create(sizeof(truck_dealer)); + array_reserve(&world->firstnames, 5); + + file_content firstname_file = platform_read_file_content("data/world/dealers.json", "rb"); + if (firstname_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(firstname_file.content); + if (!json_object) return false; + + cJSON *dealer_entry; + cJSON_ArrayForEach(dealer_entry, json_object) + { + truck_dealer new_dealer; + new_dealer.trucks = array_create(sizeof(truck)); + + cJSON* name = cJSON_GetObjectItem(dealer_entry, "name"); + if (!name) return false; + string_copyn(new_dealer.name, name->valuestring, MAX_DEALER_NAME_LENGTH); + + cJSON* logo = cJSON_GetObjectItem(dealer_entry, "logo"); + if (!logo) return false; + new_dealer.logo = assets_find_image_ref(0, assets_hash_path(logo->valuestring)); + if (!new_dealer.logo) return false; + + cJSON* trucks = cJSON_GetObjectItem(dealer_entry, "trucks"); + + cJSON *truck_entry; + cJSON_ArrayForEach(truck_entry, trucks) + { + truck new_truck; + new_truck.assigned_employee = 0; + + cJSON* logo = cJSON_GetObjectItem(truck_entry, "logo"); + if (!logo) return false; + new_truck.logo = assets_find_image_ref(0, assets_hash_path(logo->valuestring)); + if (!new_truck.logo) return false; + + cJSON* name = cJSON_GetObjectItem(truck_entry, "name"); + if (!name) return false; + string_copyn(new_truck.name, name->valuestring, MAX_TRUCK_NAME_LENGTH); + new_truck.type = assets_hash_path(new_truck.name); + + cJSON* hp = cJSON_GetObjectItem(truck_entry, "power"); + if (!hp) return false; + new_truck.hp = hp->valueint; + + cJSON* price = cJSON_GetObjectItem(truck_entry, "price"); + if (!price) return false; + new_truck.price = price->valueint; + + cJSON* fuelcapacity = cJSON_GetObjectItem(truck_entry, "fuelcapacity"); + if (!fuelcapacity) return false; + new_truck.fuelcapacity = fuelcapacity->valueint; + + cJSON* torque = cJSON_GetObjectItem(truck_entry, "torque"); + if (!torque) return false; + new_truck.torque = torque->valueint; + + cJSON* fuelusage = cJSON_GetObjectItem(truck_entry, "fuelusage"); + if (!fuelusage) return false; + new_truck.fuelusage = fuelusage->valuedouble; + + array_push(&new_dealer.trucks, &new_truck); + } + + array_push(&world->truck_dealers, &new_dealer); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&firstname_file); + + world_create_connections(world); + + return true; +} + +static bool world_load_firstnames_from_file(world* world) +{ + world->firstnames = array_create(MAX_ENPOLYEE_FIRSTNAME_LENGTH); + array_reserve(&world->firstnames, 5000); + + file_content firstname_file = platform_read_file_content("data/world/first-names.json", "rb"); + if (firstname_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(firstname_file.content); + if (!json_object) return false; + + cJSON *name_entry; + cJSON_ArrayForEach(name_entry, json_object) + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH]; + string_copyn(buffer, name_entry->valuestring, MAX_ENPOLYEE_FIRSTNAME_LENGTH); + array_push(&world->firstnames, buffer); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&firstname_file); + + world_create_connections(world); + + return true; +} + +static bool world_load_locations_from_file(world* world) +{ + world->locations = array_create(sizeof(world_location)); + file_content locations_file = platform_read_file_content("data/world/locations.json", "rb"); + if (locations_file.file_error) return false; + + cJSON *json_object = cJSON_Parse(locations_file.content); + if (!json_object) return false; + + cJSON *location_entry; + cJSON_ArrayForEach(location_entry, json_object) + { + cJSON* name = cJSON_GetObjectItem(location_entry, "name"); + if (!name) return false; + cJSON* size = cJSON_GetObjectItem(location_entry, "size"); + if (!size) return false; + cJSON* latitude = cJSON_GetObjectItem(location_entry, "latitude"); + if (!latitude) return false; + cJSON* longitude = cJSON_GetObjectItem(location_entry, "longitude"); + if (!longitude) return false; + + world_location location = world_create_location(size->valueint, latitude->valuedouble, longitude->valuedouble, name->valuestring, false); + array_push(&world->locations, &location); + } + + cJSON_Delete(json_object); + platform_destroy_file_content(&locations_file); + + world_create_connections(world); + + return true; +} + +float world_location_get_price(world_location* location) +{ + float base_price = 300000.0f; + return base_price / location->size; +} + +static money_data_collection* get_current_insights_data_for_location(world* world, world_location* location) +{ + s32 index = world->current_time.tm_year - location->purchase_year; + if (index >= location->insights.length) { + money_data_collection data = {0}; + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + data.months[i].total_income = NAN; + } + array_push(&location->insights, &data); + } + + return (money_data_collection*)array_at(&location->insights, index); +} + +static money_data_collection* get_current_insights_data(world* world) +{ + s32 index = world->current_time.tm_year - world->start_year; + if (index >= world->insights.length) { + money_data_collection data = {0}; + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + data.months[i].total_income = NAN; + } + array_push(&world->insights, &data); + } + + return (money_data_collection*)array_at(&world->insights, index); +} + +world* world_create_new() +{ + world* new_world = mem_alloc(sizeof(world)); + new_world->simulation_time = time(NULL); + new_world->start_year = gmtime(&new_world->simulation_time)->tm_year; + new_world->money = 100000.0f; + new_world->next_id = 1; + new_world->active_jobs = array_create(sizeof(active_job)); + new_world->investments = (company_investments){0}; + new_world->simulation_speed = 1; + new_world->days_since_last_random_event = 300;//-365; // No random events in the first year. + new_world->log.events = array_create(sizeof(event)); + new_world->log.write_cursor = 0; + new_world->log.has_unread_messages = false; + array_reserve(&new_world->log.events, LOG_HISTORY_LENGTH); + new_world->insights = array_create(sizeof(money_data_collection)); + array_reserve(&new_world->insights, 10); + new_world->insights.reserve_jump = 10; + array_reserve(&new_world->active_jobs, 100); + srand(new_world->simulation_time); + new_world->current_time = *gmtime(&new_world->simulation_time); + + if (!world_load_locations_from_file(new_world)) { + log_info("Failed to load locations from data/world/locations.json"); + mem_free(new_world); + return 0; + } + + if (!world_load_companies_from_file(new_world)) { + log_info("Missing companies in data/world/companies.json"); + mem_free(new_world); + return 0; + } + + if (!world_load_firstnames_from_file(new_world)) { + log_info("Missing names in data/world/first-names.json"); + mem_free(new_world); + return 0; + } + + if (!world_load_lastnames_from_file(new_world)) { + log_info("Missing names in data/world/last-names.json"); + mem_free(new_world); + return 0; + } + + if (!world_load_trucks_from_file(new_world)) { + log_info("Missing names in data/world/dealers.json"); + mem_free(new_world); + return 0; + } + + if (!world_load_boat_routes_from_file(new_world)) { + log_info("Missing names in data/world/boat-routes.json"); + mem_free(new_world); + return 0; + } + + if (!world_create_default_state(new_world)) { + log_info("Could not create world"); + mem_free(new_world); + return 0; + } + + world_assign_new_job_offers(new_world); + enable_insights_for_current_month(new_world); + + return new_world; +} + +static vec2f coords_to_px(platform_window* window, double lon, double lat) +{ + vec2f extra = {9 * scale, 4 * scale}; + vec2f map_pos = {area.x + (float)(area.w * (180.0f + lon) / 360.0f), + area.y + (float)(area.h * (90.0f - lat) / 180.0f)}; + map_pos.x += extra.x; + map_pos.y += extra.y; + return map_pos; +} + +static void world_remove_expired_job_offers(world* world) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + + for (s32 x = 0; x < location->job_offers.length; x++) + { + job_offer* offer = array_at(&location->job_offers, x); + if (offer->expire_date <= world->simulation_time) { + // TODO: only free here when not scheduled! + //array_destroy(&offer->connections); + array_remove_at(&location->job_offers, x); + x--; + } + } + } +} + +bool job_offer_has_ship_day(job_offer* offer, weekday day_to_find) +{ + for (s32 s = 0; s < offer->shipday_count; s++) { + weekday day = offer->shipdays[s]; + if (day == day_to_find) return true; + } + return false; +} + +static void world_find_location_deep(s32 depth, world_location* source, array* buf) +{ + world_location* current_source = source; + while (buf->length < depth) { + world_location* best_match = 0; + s32 attempt = 0; + + try_again: + best_match = 0; + attempt++; + s32 rand_conn = get_random_number(0, current_source->connections.length); + world_location* connection = *(world_location**)array_at(¤t_source->connections, rand_conn); + + bool already_in_path = false; + for (s32 c = 0; c < buf->length; c++) { + if (*(world_location**)array_at(buf, c) == connection) already_in_path = true; + } + + if (already_in_path && attempt < current_source->connections.length) { + goto try_again; + } + best_match = already_in_path ? 0 : connection; + + #if 0 + double best_match_distance = 0; + + for (s32 i = 0; i < current_source->connections.length; i++) { + world_location* connection = *(world_location**)array_at(¤t_source->connections, i); + if (connection == source) continue; + + bool already_in_path = false; + for (s32 c = 0; c < buf->length; c++) { + if (*(world_location**)array_at(buf, c) == connection) already_in_path = true; + } + + if (!already_in_path) { + double total_distance = fabs(connection->latitude - current_source->latitude) + fabs(connection->longitude - current_source->longitude); + + if (!best_match || best_match_distance < total_distance) { + best_match_distance = total_distance; + best_match = connection; + } + } + } + #endif + + if (!best_match) break; + array_push(buf, &best_match); + current_source = best_match; + } +} + +static void world_assign_new_job_offers(world* world) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + if (location->job_offers.length >= MAX_JOBOFFER_COUNT) continue; + + s32 company_id = get_random_number(0, world->companies.length); + company* company = array_at(&world->companies, company_id); + s32 product_id = get_random_number(0, company->products.length); + product* product = array_at(&company->products, product_id); + + job_offer new_offer = (job_offer){world->next_id++, world->simulation_time+DAYS(10), company, product, 0, {DAY_INVALID,DAY_INVALID,DAY_INVALID,DAY_INVALID}, 0}; + new_offer.connections = array_create(sizeof(world_location*)); + + s32 amount_of_shipdays = get_random_number(1, MAX_SHIPDAYS+1); + for (s32 s = 0; s < amount_of_shipdays; s++) { + s32 random_day = get_random_number(0, NUM_DAYS); + if (!job_offer_has_ship_day(&new_offer, random_day)) { + new_offer.shipdays[new_offer.shipday_count++] = random_day; + } + else { + s--; + } + } + + s32 amount_of_connections = get_random_number(1, 5) + 1; // +1 because we add original location to connection list. + + array_push(&new_offer.connections, &location); + world_find_location_deep(amount_of_connections, location, &new_offer.connections); + + float total_dist = 0.0; + for (s32 d = 0; d < new_offer.connections.length-1; d++) + { + world_location* source = *(world_location**)array_at(&new_offer.connections, d); + world_location* dest = *(world_location**)array_at(&new_offer.connections, d+1); + total_dist += distance_between_location(source, dest); + } + new_offer.total_distance = total_dist; + new_offer.reward = (u32)((JOB_OFFER_REWARD_PER_CONNECTION * location->reliability) * amount_of_connections-1); // -1 because source is is connection list. + + // lest assume most experienced drivers drive at 95km/h + double min_duration_hours = (new_offer.total_distance/95.0); + double max_diration_hours = min_duration_hours * SHIPTIME_DURATION_MULTIPLIER_MIN; + + new_offer.duration_sec_min = (time_t)(min_duration_hours * 60 * 60); + new_offer.duration_sec_max = (time_t)(max_diration_hours * 60 * 60); + + // printf("Distance: %.0f, duration between %.2f and %.2f hours.", total_dist, min_duration_hours, max_diration_hours); + + array_push(&location->job_offers, &new_offer); + } +} + +static void world_assign_resumes_to_locations(world* world) +{ + // Expire old ones. + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + + for (s32 r = 0; r < location->resumes.length; r++) + { + resume* resume = array_at(&location->resumes, r); + + // It is assumed a simulation day is longer than RESUME_FADEOUT_MS + // else the animation will be cut short. + if (resume->animation.started) { + array_remove_at(&location->resumes, r); + r--; + } + + if (resume->expire_date <= world->simulation_time) { + resume->animation.started = true; + resume->hired = false; + } + } + } + + // Assign new ones. + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + // 0.5 reliability = 0% + // 1.0 reliability = 10% + // 1.5 reliability = 20% + // etc. + // % = change of a new resume being submitted per day. + #if 1 + if (get_random_number(0, 10) >= ceil(location->reliability)) continue; + #endif + resume new_resume; + new_resume.animation = animation_create(RESUME_FADEOUT_MS); + new_resume.expire_date = world->simulation_time+DAYS(10); + new_resume.employee = create_employee(world, location); + + array_push(&location->resumes, &new_resume); + } +} + +static double distance_between_location(world_location* location1, world_location* location2) +{ + double lat1 = location1->latitude; + double lat2 = location2->latitude; + double lon1 = location1->longitude; + double lon2 = location2->longitude; + + double rlat1 = M_PI*lat1/180; + double rlat2 = M_PI*lat2/180; + double theta = lon1 - lon2; + double rtheta =M_PI*theta/180; + double dist = + sin(rlat1)*sin(rlat2) +cos(rlat1)* + cos(rlat2)*cos(rtheta); + dist = acos(dist); + dist = dist*180/M_PI; + dist = dist*60*1.1515; + return dist*1.609344; // Miles to km +} + +static float get_employee_experience_factor(employee* emp) +{ + float experience_factor = emp->experience/MAX_EFFECTIVE_EXPERIENCE; + if (experience_factor > 1.0f) experience_factor = 1.0f; + return experience_factor; +} + +static float get_shiptime_factor(employee* emp) +{ + float experience_factor = get_employee_experience_factor(emp); + float shiptime_factor = (SHIPTIME_DURATION_MULTIPLIER_MIN - (SHIPTIME_DURATION_MULTIPLIER_MIN - SHIPTIME_DURATION_MULTIPLIER_MAX) * experience_factor); + return shiptime_factor; +} + +static void world_start_scheduled_job(world* world, scheduled_job* scheduled_job, scheduled_job_time scheduled_time) +{ + if (!scheduled_time.assignee) { + scheduled_job->trust -= MISSED_DELIVERY_TRUST_PENALTY; + char error_msg[MAX_EVENT_MESSAGE_LENGTH]; + snprintf(error_msg, MAX_EVENT_MESSAGE_LENGTH, "A shipment has been missed because there is no assignee for the timeslot."); + world_report_event_ex(world, error_msg, EVENT_TYPE_MISSED_SHIPMENT_NO_ASSIGNEE, scheduled_job, scheduled_time); + return; + } + + if (!scheduled_time.assignee->assigned_truck) { + scheduled_job->trust -= MISSED_DELIVERY_TRUST_PENALTY; + char error_msg[MAX_EVENT_MESSAGE_LENGTH]; + snprintf(error_msg, MAX_EVENT_MESSAGE_LENGTH, "%s missed a shipment because they do not have a truck.", scheduled_time.assignee->name); + world_report_event(world, error_msg, EVENT_TYPE_MISSED_SHIPMENT_NO_TRUCK, scheduled_time.assignee); + return; + } + + world_location* orig = *(world_location**)array_at(&scheduled_job->offer.connections, 0); + if (scheduled_time.assignee->current_location_id != orig->id) { + scheduled_job->trust -= MISSED_DELIVERY_TRUST_PENALTY; + char error_msg[MAX_EVENT_MESSAGE_LENGTH]; + snprintf(error_msg, MAX_EVENT_MESSAGE_LENGTH, "%s missed a shipment because they are not at the right location.", scheduled_time.assignee->name); + world_report_event_ex(world, error_msg, EVENT_TYPE_MISSED_SHIPMENT_NOT_AT_LOCATION, scheduled_job, scheduled_time); + return; + } + + // Remove employee from employee list when current location is external. + if (scheduled_time.assignee->current_location_id != scheduled_time.assignee->original_location_id) { + world_location* current_loc = get_world_location_by_id(world, scheduled_time.assignee->current_location_id); + log_assert(current_loc, "Current location cannot be 0"); + employee* emp = get_employee_by_id(current_loc, scheduled_time.assignee->id); + if (emp) { + array_remove_by(¤t_loc->employees, &emp); + } + } + + scheduled_time.assignee->current_location_id = INVALID_ID; + scheduled_time.assignee->active_job_id = scheduled_job->offer.id; + + active_job new_job; + new_job.stay_at_destination = scheduled_time.stay_at_destination; + new_job.day = scheduled_time.day; + new_job.timeslot = scheduled_time.timeslot; + new_job.offer = scheduled_job->offer; + new_job.assignee = *scheduled_time.assignee; + new_job.assigned_truck = *scheduled_time.assignee->assigned_truck; + + time_t leave_time = world->simulation_time-(world->simulation_time%(15*60)); + new_job.left_at = leave_time; + + float shiptime_factor = get_shiptime_factor(&new_job.assignee); + new_job.duration_sec = new_job.offer.duration_sec_min * shiptime_factor; + + new_job.done_at = new_job.left_at + new_job.duration_sec; + new_job.reversed = false; + + float gasprice = ((new_job.offer.total_distance/100.0f) * new_job.assigned_truck.fuelusage) * DIESEL_PRICE_PER_LITER; + ADD_EXPENSE(world, orig, expenses_from_fuel, gasprice); + + array_push(&world->active_jobs, &new_job); + + audio_set_sound_volume(snd_accelerate, 0.08f); + audio_play_sound(snd_accelerate, AUDIO_CHANNEL_SFX_3); +} + +static void world_start_scheduled_jobs(world* world, s32 day, s32 timeslot) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + for (s32 r = 0; r < location->schedule.jobs.length; r++) + { + scheduled_job* scheduled_job = array_at(&location->schedule.jobs, r); + + for (s32 t = 0; t < scheduled_job->offer.shipday_count; t++) { + scheduled_job_time scheduled_time = scheduled_job->timeslots[t]; + + if (scheduled_time.day == day && scheduled_time.timeslot == timeslot) { + world_start_scheduled_job(world, scheduled_job, scheduled_time); + } + } + } + } +} + +static employee* get_global_employee_by_id(world* world, u32 id) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + for (s32 i = 0; i < location->employees.length; i++) + { + employee* em = *(employee**)array_at(&location->employees, i); + if (em->id == id) return em; + } + } + return 0; +} + +static employee* get_employee_by_id(world_location* location, u32 id) +{ + for (s32 i = 0; i < location->employees.length; i++) + { + employee* em = *(employee**)array_at(&location->employees, i); + if (em->id == id) return em; + } + return 0; +} + +static job_offer* get_job_offer_by_id(world_location* location, u32 id) +{ + for (s32 i = 0; i < location->job_offers.length; i++) + { + job_offer* job = array_at(&location->job_offers, i); + if (job->id == id) return job; + } + return 0; +} + +static active_job* get_active_job_by_id(world* world, u32 id) +{ + for (s32 i = 0; i < world->active_jobs.length; i++) + { + active_job* job = array_at(&world->active_jobs, i); + if (job->offer.id == id) return job; + } + return 0; +} + +static active_job* get_active_job_by_ref(world* world, active_job_ref ref) +{ + for (s32 i = 0; i < world->active_jobs.length; i++) + { + active_job* job = array_at(&world->active_jobs, i); + if (job->offer.id == ref.offerid && job->day == ref.day && job->timeslot == ref.timeslot) return job; + } + return 0; +} + +static scheduled_job* get_scheduled_job_by_id(world_location* location, u32 id) +{ + for (s32 i = 0; i < location->schedule.jobs.length; i++) + { + scheduled_job* scheduled_job = array_at(&location->schedule.jobs, i); + if (scheduled_job->offer.id == id) return scheduled_job; + } + return 0; +} + +static void world_update_active_jobs(world* world) +{ + for (s32 i = 0; i < world->active_jobs.length; i++) + { + active_job* job = array_at(&world->active_jobs, i); + + if (job->done_at <= world->simulation_time) { + job_endpoints endpoints = job_offer_get_endpoints(&job->offer); + scheduled_job* sc_job = get_scheduled_job_by_id(endpoints.source, job->offer.id); // Get scheduled job from source location + + if (job->reversed || job->stay_at_destination) { // Driver has returned from the round-trip or will stay at location. + world_location* orig_location = get_world_location_by_id(world, job->assignee.original_location_id); + employee* e = get_employee_by_id(orig_location, job->assignee.id); + e->current_location_id = job->stay_at_destination ? endpoints.dest->id : endpoints.source->id; + e->active_job_id = INVALID_ID; + + // Employee is scheduled to stay at location + if (job->stay_at_destination) { + array_push(&endpoints.dest->employees, &e); + } + // Employee started the job from an external location and are returning + if (job->reversed && orig_location->id != e->current_location_id) { + array_push(&endpoints.source->employees, &e); + } + + // Remove job. + array_remove_at(&world->active_jobs, i); + i--; + } + else { // Job is done, return to original location if not staying. + if (sc_job) sc_job->trust += (0.1f / sc_job->trust); // If job is still available, update trust. + ADD_INCOME(world, endpoints.source, income_from_trips, job->offer.reward); + + float gasprice = ((job->offer.total_distance/100.0f) * job->assigned_truck.fuelusage) * DIESEL_PRICE_PER_LITER; + ADD_EXPENSE(world, endpoints.source, expenses_from_fuel, gasprice); + + job->left_at = job->done_at; + job->done_at = job->left_at + job->duration_sec; + job->reversed = true; + } + } + } +} + +static float get_worked_hours_per_week_for_employee(world* world, employee* emp, scheduled_job* excluding) { + float total = 0; + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + for (s32 f = 0; f < location->schedule.jobs.length; f++) { + scheduled_job* job = array_at(&location->schedule.jobs, f); + if (job == excluding) continue; + + for (s32 s = 0; s < MAX_SHIPDAYS; s++) { + scheduled_job_time slot = job->timeslots[s]; + if (slot.assignee == emp) { + float hworked = job->offer.duration_sec_min * get_shiptime_factor(emp); + if (!slot.stay_at_destination) hworked *= 2; + total += hworked; + } + } + } + } + return total / 3600.0f; +} + +// Run once per day. +static void world_update_employee_happiness(world* world, employee* emp) { + // Calculate overworking + float max_weekly_hours = MAX_WORKED_HOURS_WEEKLY; + float hours_worked = get_worked_hours_per_week_for_employee(world, emp, 0); + float hours_overworked = hours_worked - max_weekly_hours; + if (hours_overworked > 0) emp->happiness = 1.0f - ((hours_overworked/3.0f)*0.2f); + else emp->happiness = 1.0f; + + // Calculate underpay + float expected_pay = BASE_PAY + (emp->experience * RAISE_PER_YEAR); + if (expected_pay > MAX_PAY) expected_pay = MAX_PAY; + float underpay = expected_pay - emp->salary; + + emp->happiness -= (underpay/100.0f)*0.2f; // 1 Star per 100 euro underpay/overpay + if (emp->happiness < 0.0f) emp->happiness = 0.0f; + if (emp->happiness > 1.0f) emp->happiness = 1.0f; + + if (emp->happiness < MINIMUM_EMPLOYEE_HAPPINESS) { + emp->days_below_happiness_treshold++; + } + else { + emp->days_below_happiness_treshold--; + } + if (emp->days_below_happiness_treshold < 0) emp->days_below_happiness_treshold = 0; + + if (emp->days_below_happiness_treshold >= EMPLOYEE_MAX_UNHAPPY_DAYS_BEFORE_QUITTING) { + + char error_msg[MAX_EVENT_MESSAGE_LENGTH]; + snprintf(error_msg, MAX_EVENT_MESSAGE_LENGTH, "%s quit their job because they were unhappy for too long.", emp->name); + world_report_event(world, error_msg, EVENT_TYPE_EMPLOYEE_QUIT, get_world_location_by_id(world, emp->original_location_id)); + + end_contract_with_employee(world, emp); + } +} + +// Pay investments daily. Run once per day. +static void world_pay_investments(world* world) +{ + ADD_EXPENSE(world, 0, expenses_from_utility, world->investments.safety/28.0f); + ADD_EXPENSE(world, 0, expenses_from_utility, world->investments.marketing/28.0f); + ADD_EXPENSE(world, 0, expenses_from_utility, world->investments.human_resources/28.0f); + ADD_EXPENSE(world, 0, expenses_from_utility, world->investments.training/28.0f); + ADD_EXPENSE(world, 0, expenses_from_utility, world->investments.legal/28.0f); +} + +// Payout salaries daily. Run once per day. +static void world_payout_salaries(world* world) +{ + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + for (s32 r = 0; r < location->employees.length; r++) + { + employee* emp = *(employee**)array_at(&location->employees, r); + + // Make sure employees are not paid twice when at external location. + if (emp->current_location_id == location->id) { + world_update_employee_happiness(world, emp); + ADD_EXPENSE(world, location, expenses_from_employees, (emp->salary/28.0f)); + } + } + } +} + +static void enable_insights_for_current_month(world* world) +{ + if (isnan(EXPENSES.total_income)) EXPENSES.total_income = 0; // Validate insights for current month. + + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + money_data_collection* collection = get_current_insights_data_for_location(world, location); + collection->months[world->current_time.tm_mon].total_income = 0; + } +} + +static void world_start_random_events(world* world) +{ + world->days_since_last_random_event++; + + // Minor events + { + #define MIN_DELAY_BETWEEN_EVENTS (60) + float change_of_random_event = 0.0f; + if (world->days_since_last_random_event > MIN_DELAY_BETWEEN_EVENTS) { + change_of_random_event = ((world->days_since_last_random_event - MIN_DELAY_BETWEEN_EVENTS)*0.5f)/100.0f; + if (change_of_random_event > 1.0f) change_of_random_event = 1.0f; + + bool run_event = change_of_random_event >= (get_random_number(0, 100)/100.0f); + + (void)run_event; // TODO start event. + } + } +} + +static void end_contract_with_employee(world* world, employee* emp) +{ + if (emp->assigned_truck) emp->assigned_truck->assigned_employee = 0; + + // Remove assigned timeslots. + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + if (!location->is_owned) continue; + + for (s32 r = 0; r < location->schedule.jobs.length; r++) + { + scheduled_job* scheduled_job = array_at(&location->schedule.jobs, r); + + for (s32 t = 0; t < scheduled_job->offer.shipday_count; t++) { + scheduled_job_time scheduled_time = scheduled_job->timeslots[t]; + + if (scheduled_time.assignee == emp) { + scheduled_job->timeslots[t].assignee = 0; + } + } + } + } + + // Remove active job + active_job* curr_job = get_active_job_by_id(world, emp->active_job_id); + array_remove_by(&world->active_jobs, curr_job); + + // Remove employee from original location + world_location* orig_loc = get_world_location_by_id(world, emp->original_location_id); + array_remove_by(&orig_loc->employees, &emp); + + // Remove employee from current location + world_location* curr_loc = get_world_location_by_id(world, emp->current_location_id); + if (curr_loc) array_remove_by(&curr_loc->employees, &emp); +} + +static void world_run_simulation_tick(world* world) +{ + s32 elapsed_sec = MINUTES(world->simulation_speed); + s64 prev_stamp = world->simulation_time; + world->simulation_time += elapsed_sec; + + struct tm prev_time_buf; + prev_time_buf = *gmtime(&prev_stamp); + struct tm* prev_time = &prev_time_buf; + + struct tm curr_time_buf; + curr_time_buf = *gmtime(&world->simulation_time); + struct tm* curr_time = &curr_time_buf; + world->current_time = curr_time_buf; + + if (prev_time->tm_wday != curr_time->tm_wday) { // Run once per day + world_remove_expired_job_offers(world); + world_assign_resumes_to_locations(world); + world_start_random_events(world); + + if (curr_time->tm_mday <= 28) { + world_payout_salaries(world); // Pay salary first 28 days of month. + world_pay_investments(world); + } + } + + if (prev_time->tm_wday == SUNDAY && curr_time->tm_wday == MONDAY) { // Run once per week + world_assign_new_job_offers(world); + } + + if (curr_time->tm_hour >= WORK_HOUR_START && curr_time->tm_hour < WORK_HOUR_END) { // Run every 15min + s32 hour_part = 60 / TIME_SLOTS_PER_HOUR; + s32 prev_part = prev_time->tm_min / hour_part; + s32 curr_part = curr_time->tm_min / hour_part; + if (prev_part != curr_part) { + world_start_scheduled_jobs(world, curr_time->tm_wday, ((curr_time->tm_hour-WORK_HOUR_START)*TIME_SLOTS_PER_HOUR) + curr_part); + } + } + + if (curr_time->tm_mday < prev_time->tm_mday) { // Run once per month + enable_insights_for_current_month(world); + } + + world_update_active_jobs(world); +} + +void world_update(platform_window* window, world* world) +{ + world_run_simulation_tick(world); +} + +static vec2f get_world_location_for_job(platform_window* window, world* world, active_job* job) +{ + time_t total_time = job->done_at - job->left_at; + time_t elapsed_time = world->simulation_time - job->left_at; + float complete_percentage = elapsed_time/(float)total_time; + if (job->reversed) complete_percentage = 1.0f - complete_percentage; + if (complete_percentage > 1.0f) complete_percentage = 1.0f; + double current_km = job->offer.total_distance*complete_percentage; + double calculating_km = 0.0; + for (s32 d = 0; d < job->offer.connections.length-1; d++) + { + world_location* source = *(world_location**)array_at(&job->offer.connections, d); + world_location* dest = *(world_location**)array_at(&job->offer.connections, d+1); + double dist_between_points = distance_between_location(source, dest); + double next_landmark = calculating_km + dist_between_points; + double dist_from_source = current_km - calculating_km; + + if (calculating_km < current_km && next_landmark >= current_km) { + float progress_between_points = dist_from_source / dist_between_points; + double lon = source->longitude + (dest->longitude - source->longitude) * progress_between_points; + double lat = source->latitude + (dest->latitude - source->latitude) * progress_between_points; + + vec2f map_pos = coords_to_px(window, lon, lat); + return map_pos; + } + else { + calculating_km = next_landmark; + } + } + return (vec2f){0.0f, 0.0f}; +} + +world_update_result world_render(platform_window* window, world* world) +{ + world_update_result result = {0,0}; + + renderer->set_render_depth(3); + // Draw locations + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* location = array_at(&world->locations, i); + vec2f map_pos = coords_to_px(window, location->longitude, location->latitude); + map_pos.x *= zoom; + map_pos.y *= zoom; + map_pos.x += camera_x; + map_pos.y += camera_y; + + location->map_position_x = map_pos.x; + location->map_position_y = map_pos.y; + + s32 circle_x = location->map_position_x - dotsize/2; + s32 circle_y = location->map_position_y - dotsize/2; + bool hovered = mouse_interacts(circle_x, circle_y, dotsize, dotsize); + location->is_hovered = hovered; + if (hovered) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) { + result.clicked_location = location; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + } + + color tint = location->is_owned ? COLOR_LOCATION_DOT_OWNED : COLOR_LOCATION_DOT_UNOWNED; + if (location->is_hovered) { + tint = COLOR_DOT_HOVERED; + } + renderer->render_image_tint(img_locationdot, circle_x, circle_y, dotsize, dotsize, tint); + } + + renderer->set_render_depth(2); + // Draw active jobs + for (s32 i = 0; i < world->active_jobs.length; i++) + { + active_job* job = array_at(&world->active_jobs, i); + vec2f pos = get_world_location_for_job(window, world, job); + job->px_pos = pos; + + s32 circle_x = job->px_pos.x - dotsize/2; + s32 circle_y = job->px_pos.y - dotsize/2; + bool hovered = mouse_interacts(circle_x, circle_y, dotsize, dotsize); + job->is_hovered = hovered; + if (hovered) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) result.clicked_job = job; + } + + renderer->render_image_tint(img_locationdot, job->px_pos.x-(dotsize/2),job->px_pos.y-(dotsize/2), dotsize,dotsize, job->is_hovered ? COLOR_DOT_HOVERED : rgb(255,0,0)); + } + + renderer->set_render_depth(1); + dotsize = 5 * scale * zoom; + // Draw connections + for (s32 i = 0; i < world->locations.length; i++) + { + world_location* source = array_at(&world->locations, i); + + for (s32 d = 0; d < source->connections.length; d++) + { + world_location* destination = *(world_location**)array_at(&source->connections, d); + if (destination == source) continue; + + renderer->render_line(source->map_position_x, source->map_position_y, destination->map_position_x, destination->map_position_y, 1, rgb(255,0,0)); + } + } + + renderer->set_render_depth(1); + update_render_scenery(world); + + return result; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2