diff --git a/.gitignore b/.gitignore index bbf313b..0953dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,11 @@ *.i*86 *.x86_64 *.hex +tbftss # Debug files *.dSYM/ + +# folders +dist +dev diff --git a/README.md b/README.md index dcd5c07..f31642d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # tbftss 2D mission-based space shooter, based on the Battle for the Solar System space opera trilogy. + +CREDITS + +GRAPHICS + +gfx/planets/earth.png - modified from the the Blue Marble NASA photograph: http://visibleearth.nasa.gov/view.php?id=57723 + +All other graphics are CC BY-NC-SA 3.0, with the following attribution: Copyright 2015, Stephen J Sweeney | www.battleforthesolarsystem.com + +SOUND + +35677__jobro__laser1.ogg - Laser 1, by jobro - https://freesound.org/people/jobro/sounds/35677/ +42106__marcuslee__laser-wrath-4.ogg - Laser Wrath 4, by marcusless - https://freesound.org/people/marcuslee/sounds/42106/ +47252__nthompson__bad-explosion.ogg - bad explosion, by nthompson - https://freesound.org/people/nthompson/sounds/47252/ +77087__supraliminal__laser-short.ogg - Laser short, by Supraliminal - https://freesound.org/people/Supraliminal/sounds/77087/ +162265__qubodup__explosive.ogg - Explosive, by qubodup - https://freesound.org/people/qubodup/sounds/162265/ +207322__animationisaac__short-explosion.ogg - Short explosion, by animationIsaac - https://freesound.org/people/animationIsaac/sounds/207322/ +254071__tb0y298__firework-explosion.ogg - Firework Explosion, by TB0Y298 - https://freesound.org/people/TB0Y298/sounds/254071/ +263621__jamesabdulrahman__permission-to-panic.ogg - Permission to panic?, by jamesabdulrahman - https://freesound.org/people/jamesabdulrahman/sounds/263621/ +268344__julien-matthey__jm-noiz-laser-01.ogg - JM_NOIZ_Laser 01.wav, by Julien Matthey - https://freesound.org/people/Julien%20Matthey/sounds/268344/ +275151__bird-man__gun-shot.ogg - Gun Shot.wav, by Bird_man - https://freesound.org/people/Bird_man/sounds/275151/ + +MUSIC + +Battle in the winter.mp3, by Johan Brodd - http://opengameart.org/content/battle-in-the-winter +battleThemeA.mp3, by cynicmusic.com | pixelsphere.org - http://opengameart.org/content/battle-theme-a +determination.mp3, by artisticdude - http://opengameart.org/content/determination +heroism.ogg, by Edward J. Blakeley (http://www.edwardblakeley.com/) - http://opengameart.org/content/heroism +Pressure.ogg, by yd - http://opengameart.org/content/pressure +Rise of Spirit, by Alexandr Zhelanov - https://soundcloud.com/alexandr-zhelanov +Showdown.mp3, by el-corleo - http://opengameart.org/content/showdown diff --git a/data/app/config.json b/data/app/config.json new file mode 100644 index 0000000..ba00589 --- /dev/null +++ b/data/app/config.json @@ -0,0 +1,8 @@ +{ + "winWidth" : 1280, + "winHeight" : 720, + "vSync" : 1, + "fullscreen" : 0, + "musicVolume" : 8, + "soundVolume" : 10 +} diff --git a/data/battle/bullets.json b/data/battle/bullets.json new file mode 100644 index 0000000..025d012 --- /dev/null +++ b/data/battle/bullets.json @@ -0,0 +1,25 @@ +[ + { + "type" : "BT_PARTICLE", + "damage" : 1, + "textureName" : "gfx/bullets/particleBolt.png", + "sound" : "SND_PARTICLE", + "flags" : "BF_NONE" + }, + + { + "type" : "BT_PLASMA", + "damage" : 3, + "textureName" : "gfx/bullets/plasmaBolt.png", + "sound" : "SND_PLASMA", + "flags" : "BF_NONE" + }, + + { + "type" : "BT_MISSILE", + "damage" : 25, + "textureName" : "gfx/bullets/missile.png", + "sound" : "SND_PLASMA", + "flags" : "BF_ENGINE" + } +] diff --git a/data/fighters/ataf.json b/data/fighters/ataf.json new file mode 100644 index 0000000..e690c39 --- /dev/null +++ b/data/fighters/ataf.json @@ -0,0 +1,41 @@ +{ + "name" : "ATAF", + "health" : 50, + "shield" : 9999, + "speed" : 3, + "reloadTime" : 6, + "shieldRechargeRate" : 5, + "textureName" : "gfx/fighters/ataf.png", + "guns" : [ + { + "type" : "BT_PLASMA", + "x" : -5, + "y" : 0 + }, + { + "type" : "BT_PLASMA", + "x" : 5, + "y" : 0 + }, + { + "type" : "BT_PLASMA", + "x" : 9, + "y" : -4 + }, + { + "type" : "BT_PLASMA", + "x" : -9, + "y" : -4 + }, + { + "type" : "BT_PLASMA", + "x" : 13, + "y" : -8 + }, + { + "type" : "BT_PLASMA", + "x" : -13, + "y" : -8 + } + ] +} diff --git a/data/fighters/dart.json b/data/fighters/dart.json new file mode 100644 index 0000000..085c9af --- /dev/null +++ b/data/fighters/dart.json @@ -0,0 +1,21 @@ +{ + "name" : "Dart", + "health" : 10, + "shield" : 0, + "speed" : 3, + "reloadTime" : 24, + "shieldRechargeRate" : 0, + "textureName" : "gfx/fighters/dart01.png", + "guns" : [ + { + "type" : "BT_PARTICLE", + "x" : -8, + "y" : 0 + }, + { + "type" : "BT_PARTICLE", + "x" : 8, + "y" : 0 + } + ] +} diff --git a/data/fighters/jackal.json b/data/fighters/jackal.json new file mode 100644 index 0000000..7135812 --- /dev/null +++ b/data/fighters/jackal.json @@ -0,0 +1,16 @@ +{ + "name" : "Jackal", + "health" : 50, + "shield" : 50, + "speed" : 2, + "reloadTime" : 14, + "shieldRechargeRate" : 45, + "textureName" : "gfx/fighters/jackal.png", + "guns" : [ + { + "type" : "BT_PLASMA", + "x" : 0, + "y" : 0 + } + ] +} diff --git a/data/fighters/list.json b/data/fighters/list.json new file mode 100644 index 0000000..f97b142 --- /dev/null +++ b/data/fighters/list.json @@ -0,0 +1,10 @@ +[ + "data/fighters/ataf.json", + "data/fighters/dart.json", + "data/fighters/jackal.json", + "data/fighters/simpleDart.json", + "data/fighters/sphinx.json", + "data/fighters/staticDart.json", + "data/fighters/taf.json", + "data/fighters/unarmedDart.json" +] diff --git a/data/fighters/simpleDart.json b/data/fighters/simpleDart.json new file mode 100644 index 0000000..12535d0 --- /dev/null +++ b/data/fighters/simpleDart.json @@ -0,0 +1,21 @@ +{ + "name" : "SimpleDart", + "health" : 10, + "shield" : 0, + "speed" : 3, + "reloadTime" : 24, + "shieldRechargeRate" : 0, + "textureName" : "gfx/fighters/dart01.png", + "guns" : [ + { + "type" : "BT_PARTICLE", + "x" : -8, + "y" : 0 + }, + { + "type" : "BT_PARTICLE", + "x" : 8, + "y" : 0 + } + ] +} diff --git a/data/fighters/sphinx.json b/data/fighters/sphinx.json new file mode 100644 index 0000000..2fb6b1e --- /dev/null +++ b/data/fighters/sphinx.json @@ -0,0 +1,16 @@ +{ + "name" : "Sphinx", + "health" : 75, + "shield" : 50, + "speed" : 1.75, + "reloadTime" : 14, + "shieldRechargeRate" : 60, + "textureName" : "gfx/fighters/sphinx.png", + "guns" : [ + { + "type" : "BT_PLASMA", + "x" : 0, + "y" : 0 + } + ] +} diff --git a/data/fighters/staticDart.json b/data/fighters/staticDart.json new file mode 100644 index 0000000..b0a493c --- /dev/null +++ b/data/fighters/staticDart.json @@ -0,0 +1,9 @@ +{ + "name" : "StaticDart", + "health" : 10, + "shield" : 0, + "speed" : 0, + "reloadTime" : 24, + "shieldRechargeRate" : 0, + "textureName" : "gfx/fighters/dart01.png" +} diff --git a/data/fighters/taf.json b/data/fighters/taf.json new file mode 100644 index 0000000..b1ac1b0 --- /dev/null +++ b/data/fighters/taf.json @@ -0,0 +1,25 @@ +{ + "name" : "TAF", + "health" : 25, + "shield" : 25, + "speed" : 2, + "reloadTime" : 16, + "shieldRechargeRate" : 50, + "textureName" : "gfx/fighters/taf.png", + "guns" : [ + { + "type" : "BT_PLASMA", + "x" : -12, + "y" : -12 + }, + { + "type" : "BT_PLASMA", + "x" : 12, + "y" : -12 + } + ], + "missiles" : { + "type" : "MISSILE_MISSILE", + "ammo" : 4 + } +} diff --git a/data/fighters/unarmedDart.json b/data/fighters/unarmedDart.json new file mode 100644 index 0000000..ea82551 --- /dev/null +++ b/data/fighters/unarmedDart.json @@ -0,0 +1,9 @@ +{ + "name" : "UnarmedDart", + "health" : 10, + "shield" : 0, + "speed" : 3, + "reloadTime" : 24, + "shieldRechargeRate" : 0, + "textureName" : "gfx/fighters/dart01.png" +} diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000..aa00de0 Binary files /dev/null and b/data/fonts/Roboto-Medium.ttf differ diff --git a/data/galaxy/starSystems.json b/data/galaxy/starSystems.json new file mode 100644 index 0000000..a3e6a64 --- /dev/null +++ b/data/galaxy/starSystems.json @@ -0,0 +1,280 @@ +{ + "starSystems": [ + { + "name": "Sol", + "side" : "SIDE_CSN", + "x": 840, + "y": 425, + "missions" : [ + "data/missions/sol/01 - free flight.json", + "data/missions/sol/02 - weapons.json", + "data/missions/sol/03 - moving target.json", + "data/missions/sol/04 - real target.json", + "data/missions/sol/05 - wingmates.json", + "data/missions/sol/06 - final.json" + ] + }, + { + "name": "Alpha Centauri", + "side" : "SIDE_CSN", + "x": 860, + "y": 404, + "missions" : [] + }, + { + "name": "Ecuador Minor", + "side" : "SIDE_CSN", + "x": 831, + "y": 333, + "missions" : [] + }, + { + "name": "Angel", + "side" : "SIDE_CSN", + "x": 690, + "y": 305, + "missions" : [] + }, + { + "name": "Indigo", + "side" : "SIDE_CSN", + "x": 760, + "y": 270, + "missions" : [] + }, + { + "name": "Rex", + "side" : "SIDE_CSN", + "x": 834, + "y": 276, + "missions" : [] + }, + { + "name": "Gabriel", + "side" : "SIDE_CSN", + "x": 860, + "y": 170, + "missions" : [] + }, + { + "name": "Darlon", + "side" : "SIDE_CSN", + "x": 808, + "y": 190, + "missions" : [] + }, + { + "name": "Granada", + "side" : "SIDE_CSN", + "x": 741, + "y": 221, + "missions" : [] + }, + { + "name": "Parish", + "side" : "SIDE_CSN", + "x": 666, + "y": 261, + "missions" : [] + }, + { + "name": "Temper", + "side" : "SIDE_CSN", + "x": 595, + "y": 260, + "missions" : [ + "data/missions/temper/01 - pirate uprising #1.json", + "data/missions/temper/02 - pirate uprising #2.json" + ] + }, + { + "name": "Lenon", + "side" : "SIDE_CSN", + "x": 655, + "y": 180, + "missions" : [] + }, + { + "name": "HD 21563010", + "side" : "SIDE_CSN", + "x": 813, + "y": 54, + "missions" : [] + }, + { + "name": "Trilliack", + "side" : "SIDE_UNF", + "x": 501, + "y": 312, + "missions" : [] + }, + { + "name": "Alba", + "side" : "SIDE_UNF", + "x": 465, + "y": 235, + "missions" : [] + }, + { + "name": "Aster", + "side" : "SIDE_UNF", + "x": 445, + "y": 145, + "missions" : [] + }, + { + "name": "Iliad", + "side" : "SIDE_UNF", + "x": 335, + "y": 135, + "missions" : [] + }, + { + "name": "Warro", + "side" : "SIDE_UNF", + "x": 289, + "y": 216, + "missions" : [] + }, + { + "name": "Coyote", + "side" : "SIDE_UNF", + "x": 325, + "y": 385, + "missions" : [] + }, + { + "name": "Rothan", + "side" : "SIDE_UNF", + "x": 366, + "y": 298, + "missions" : [] + }, + { + "name": "Donesta", + "side" : "SIDE_UNF", + "x": 527, + "y": 161, + "missions" : [] + }, + { + "name": "India", + "side" : "SIDE_UNF", + "x": 539, + "y": 427, + "missions" : [] + }, + { + "name": "Antomis", + "side" : "SIDE_UNF", + "x": 582, + "y": 115, + "missions" : [] + }, + { + "name": "Mace", + "side" : "SIDE_UNF", + "x": 444, + "y": 359, + "missions" : [] + }, + { + "name": "Clarke", + "side" : "SIDE_UNF", + "x": 398, + "y": 84, + "missions" : [] + }, + { + "name": "Adetton", + "side" : "SIDE_INF", + "x": 228, + "y": 228, + "missions" : [] + }, + { + "name": "Atlante", + "side" : "SIDE_INF", + "x": 212, + "y": 85, + "missions" : [] + }, + { + "name": "Carthege", + "side" : "SIDE_INF", + "x": 163, + "y": 122, + "missions" : [] + }, + { + "name": "Codexa", + "side" : "SIDE_INF", + "x": 195, + "y": 101, + "missions" : [] + }, + { + "name": "Haylahe", + "side" : "SIDE_INF", + "x": 204, + "y": 132, + "missions" : [] + }, + { + "name": "Kethlan", + "side" : "SIDE_INF", + "x": 95, + "y": 95, + "missions" : [] + }, + { + "name": "Krasst", + "side" : "SIDE_INF", + "x": 135, + "y": 305, + "missions" : [] + }, + { + "name": "Mekel", + "side" : "SIDE_INF", + "x": 228, + "y": 113, + "missions" : [] + }, + { + "name": "Phylent", + "side" : "SIDE_INF", + "x": 275, + "y": 165, + "missions" : [] + }, + { + "name": "Sampi-Persei VII", + "side" : "SIDE_INF", + "x": 342, + "y": 182, + "missions" : [] + }, + { + "name": "Tigris", + "side" : "SIDE_INF", + "x": 269, + "y": 274, + "missions" : [] + }, + { + "name": "Troy", + "side" : "SIDE_INF", + "x": 107, + "y": 269, + "missions" : [] + }, + { + "name": "Pearl", + "side" : "SIDE_INF", + "x": 133, + "y": 215, + "missions" : [] + } + ] +} diff --git a/data/missions/sol/01 - free flight.json b/data/missions/sol/01 - free flight.json new file mode 100644 index 0000000..18ff9ab --- /dev/null +++ b/data/missions/sol/01 - free flight.json @@ -0,0 +1,13 @@ +{ + "name" : "Free Flight", + "description" : "A simple test flight. Get used to piloting your fighter, without threat of attack or any other dangers to face. There are no objectives to complete in this mission, so you may quit it at any time and move on to the next one.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + } +} diff --git a/data/missions/sol/02 - weapons.json b/data/missions/sol/02 - weapons.json new file mode 100644 index 0000000..26eca49 --- /dev/null +++ b/data/missions/sol/02 - weapons.json @@ -0,0 +1,29 @@ +{ + "name" : "Weapons Training", + "description" : "Practice using your fighter's weapons against a stationary target.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "objectives" : [ + { + "description" : "Destroy Dart", + "targetName" : "Dart", + "targetValue" : 1 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + }, + "fighters" : [ + { + "name" : "Dart", + "type" : "StaticDart", + "side" : "SIDE_PIRATE", + "x" : 800, + "y" : 200 + } + ] +} diff --git a/data/missions/sol/03 - moving target.json b/data/missions/sol/03 - moving target.json new file mode 100644 index 0000000..07106a2 --- /dev/null +++ b/data/missions/sol/03 - moving target.json @@ -0,0 +1,29 @@ +{ + "name" : "Moving Target", + "description" : "Most targets you face in the field will be moving. This course gives you the chance to practice taking down an enemy that moves around.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "objectives" : [ + { + "description" : "Eliminate Dart", + "targetName" : "Dart", + "targetValue" : 1 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + }, + "fighters" : [ + { + "name" : "Dart", + "type" : "UnarmedDart", + "side" : "SIDE_PIRATE", + "x" : 640, + "y" : 0 + } + ] +} diff --git a/data/missions/sol/04 - real target.json b/data/missions/sol/04 - real target.json new file mode 100644 index 0000000..69d875d --- /dev/null +++ b/data/missions/sol/04 - real target.json @@ -0,0 +1,29 @@ +{ + "name" : "Active Target", + "description" : "Face off against an opponent that is armed. The enemy in this course is a Dart, kitted out with twin particle cannons. It shouldn't prove too difficult a challenge.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "objectives" : [ + { + "description" : "Eliminate Dart", + "targetName" : "Dart", + "targetValue" : 1 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + }, + "fighters" : [ + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 640, + "y" : -1000 + } + ] +} diff --git a/data/missions/sol/05 - wingmates.json b/data/missions/sol/05 - wingmates.json new file mode 100644 index 0000000..ad3514e --- /dev/null +++ b/data/missions/sol/05 - wingmates.json @@ -0,0 +1,57 @@ +{ + "name" : "Wingmates", + "description" : "Battle along side two team mates to take on 3 enemy fighters.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "objectives" : [ + { + "description" : "Eliminate Darts", + "targetName" : "Dart", + "targetValue" : 3 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + }, + "fighters" : [ + { + "name" : "TAF", + "type" : "TAF", + "side" : "SIDE_ALLIES", + "x" : 440, + "y" : 480 + }, + { + "name" : "TAF", + "type" : "TAF", + "side" : "SIDE_ALLIES", + "x" : 840, + "y" : 480 + }, + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 2640, + "y" : -1500 + }, + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 2640, + "y" : -1500 + }, + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 2640, + "y" : -1500 + } + ] +} diff --git a/data/missions/sol/06 - final.json b/data/missions/sol/06 - final.json new file mode 100644 index 0000000..a396c18 --- /dev/null +++ b/data/missions/sol/06 - final.json @@ -0,0 +1,57 @@ +{ + "name" : "3 Against 1", + "description" : "Take on three enemy targets single handedly. Once again, these are Darts equipped with simple particle cannons. A single TAF should not find itself outclassed here, so long as the enemy are not allowed to gain the upper hand.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/earth.png", + "music" : "music/Battle in the winter.mp3", + "objectives" : [ + { + "description" : "Eliminate Darts", + "targetName" : "Dart", + "targetValue" : 3 + } + ], + "challenges" : [ + { + "type" : "CHALLENGE_ARMOUR", + "targetValue" : 100 + }, + { + "type" : "CHALLENGE_TIME", + "targetValue" : 30 + }, + { + "type" : "CHALLENGE_ACCURACY", + "targetValue" : 25 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Jim Goddard", + "squadron" : "Midnight Runners" + }, + "fighters" : [ + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 640, + "y" : -1500 + }, + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 640, + "y" : -1500 + }, + { + "name" : "Dart", + "type" : "SimpleDart", + "side" : "SIDE_PIRATE", + "x" : 640, + "y" : -1500 + } + ] +} diff --git a/data/missions/temper/01 - pirate uprising #1.json b/data/missions/temper/01 - pirate uprising #1.json new file mode 100644 index 0000000..7732fa7 --- /dev/null +++ b/data/missions/temper/01 - pirate uprising #1.json @@ -0,0 +1,68 @@ +{ + "name" : "Pirate Uprising #1", + "description" : "The Pandoran's push in to Independent space has led to a surge in pirate activity along the Confederation border. The threat needs to be dealt with immediately, starting with cutting back on the enemy presense at Temper.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/spirit.png", + "music" : "music/battleThemeA.mp3", + "objectives" : [ + { + "description" : "Eliminate Darts", + "targetName" : "Dart", + "targetValue" : 30 + } + ], + "challenges" : [ + { + "type" : "CHALLENGE_NO_LOSSES", + "targetValue" : 0 + }, + { + "type" : "CHALLENGE_ARMOUR", + "targetValue" : 100 + }, + { + "type" : "CHALLENGE_PLAYER_KILLS", + "targetValue" : 15 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Katherine Strickland", + "squadron" : "Steel Bulls" + }, + "fighterGroups" : [ + { + "name" : "Ally", + "type" : "TAF", + "number" : 4, + "side" : "SIDE_ALLIES", + "x" : 0, + "y" : 0 + }, + { + "type" : "SimpleDart", + "name" : "Dart", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : 1000, + "y" : 5000 + }, + { + "type" : "SimpleDart", + "name" : "Dart", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : -3000, + "y" : -3000 + }, + { + "type" : "Dart", + "name" : "Dart", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : 0, + "y" : -6000 + } + ] +} diff --git a/data/missions/temper/02 - pirate uprising #2.json b/data/missions/temper/02 - pirate uprising #2.json new file mode 100644 index 0000000..555182b --- /dev/null +++ b/data/missions/temper/02 - pirate uprising #2.json @@ -0,0 +1,68 @@ +{ + "name" : "Pirate Uprising #2", + "description" : "Despite recent efforts, the pirate and insurgent activity shows no sign of abating. We need to continue to smash the rings as we find them and prevent them from gaining a foothold in Temper, as this could only be bad for morale and the war effort. While we still face Darts, the modifications to these craft are becoming more prevelant, and there are more shielded and plasma-equipped fighters to be found in their ranks.", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/spirit.png", + "music" : "music/battleThemeA.mp3", + "objectives" : [ + { + "description" : "Eliminate Darts", + "targetName" : "Dart", + "targetValue" : 36 + } + ], + "challenges" : [ + { + "type" : "CHALLENGE_1_LOSS", + "targetValue" : 0 + }, + { + "type" : "CHALLENGE_ARMOUR", + "targetValue" : 80 + }, + { + "type" : "CHALLENGE_PLAYER_KILLS", + "targetValue" : 10 + } + ], + "player" : { + "type" : "TAF", + "side" : "SIDE_ALLIES", + "pilot" : "Katherine Strickland", + "squadron" : "Steel Bulls" + }, + "fighterGroups" : [ + { + "name" : "Ally", + "type" : "TAF", + "number" : 4, + "side" : "SIDE_ALLIES", + "x" : 0, + "y" : 0 + }, + { + "type" : "Dart", + "name" : "Dart", + "number" : 12, + "side" : "SIDE_PIRATE", + "x" : -3000, + "y" : 0 + }, + { + "type" : "Dart", + "name" : "Dart", + "number" : 12, + "side" : "SIDE_PIRATE", + "x" : 4000, + "y" : 0 + }, + { + "type" : "Dart", + "name" : "Dart", + "number" : 12, + "side" : "SIDE_PIRATE", + "x" : 0, + "y" : 6000 + } + ] +} diff --git a/data/missions/test/test.json b/data/missions/test/test.json new file mode 100644 index 0000000..439bf2f --- /dev/null +++ b/data/missions/test/test.json @@ -0,0 +1,90 @@ +{ + "name" : "Operation Menelaus", + "description" : "", + "background" : "gfx/backgrounds/background03.jpg", + "planet" : "gfx/planets/spirit.png", + "music" : "music/battleThemeA.mp3", + "objectives" : [ + { + "description" : "Eliminate Enemies", + "targetName" : "Enemy", + "targetValue" : 100 + } + ], + "challenges" : [ + { + "type" : "CHALLENGE_NO_LOSSES", + "targetValue" : 0 + }, + { + "type" : "CHALLENGE_ARMOUR", + "targetValue" : 80 + }, + { + "type" : "CHALLENGE_PLAYER_KILLS", + "targetValue" : 25 + } + ], + "player" : { + "type" : "ATAF", + "side" : "SIDE_CSN" + }, + "fighterGroups" : [ + { + "name" : "Ally", + "type" : "TAF", + "number" : 16, + "side" : "SIDE_CSN", + "x" : 0, + "y" : 0 + }, + { + "type" : "Jackal", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : 1000, + "y" : 5000 + }, + { + "type" : "Jackal", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : 2500, + "y" : 5000 + }, + { + "type" : "Jackal", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : 4000, + "y" : 5000 + }, + { + "type" : "Sphinx", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : -1000, + "y" : 5000 + }, + { + "type" : "Sphinx", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : -2500, + "y" : 5000 + }, + { + "type" : "Sphinx", + "name" : "Enemy", + "number" : 10, + "side" : "SIDE_PIRATE", + "x" : -5000, + "y" : 5000 + } + ] +} diff --git a/data/widgets/battleLost.json b/data/widgets/battleLost.json new file mode 100644 index 0000000..4c6f187 --- /dev/null +++ b/data/widgets/battleLost.json @@ -0,0 +1,22 @@ +[ + { + "name" : "retry", + "group" : "battleLost", + "type" : "WT_BUTTON", + "text" : "Retry", + "x" : 490, + "y" : 680, + "w" : 150, + "h": 34 + }, + { + "name" : "quit", + "group" : "battleLost", + "type" : "WT_BUTTON", + "text" : "Quit", + "x" : 790, + "y" : 680, + "w" : 150, + "h": 34 + } +] diff --git a/data/widgets/battleWon.json b/data/widgets/battleWon.json new file mode 100644 index 0000000..555b657 --- /dev/null +++ b/data/widgets/battleWon.json @@ -0,0 +1,22 @@ +[ + { + "name" : "continue", + "group" : "battleWon", + "type" : "WT_BUTTON", + "text" : "Continue", + "x" : 490, + "y" : 680, + "w" : 150, + "h": 34 + }, + { + "name" : "retry", + "group" : "battleWon", + "type" : "WT_BUTTON", + "text" : "Retry", + "x" : 790, + "y" : 680, + "w" : 150, + "h": 34 + } +] diff --git a/data/widgets/galacticMap.json b/data/widgets/galacticMap.json new file mode 100644 index 0000000..0fa2b92 --- /dev/null +++ b/data/widgets/galacticMap.json @@ -0,0 +1,43 @@ +[ + { + "name" : "resume", + "group" : "galacticMap", + "type" : "WT_BUTTON", + "text" : "Resume", + "x" : -1, + "y" : 215, + "w" : 200, + "h": 34 + }, + { + "name" : "stats", + "group" : "galacticMap", + "type" : "WT_BUTTON", + "text" : "Stats", + "x" : -1, + "y" : 315, + "w" : 200, + "h": 34 + }, + { + "name" : "options", + "group" : "galacticMap", + "type" : "WT_BUTTON", + "text" : "Options", + "x" : -1, + "y" : 415, + "w" : 200, + "h": 34 + }, + { + "name" : "quit", + "group" : "galacticMap", + "type" : "WT_BUTTON", + "text" : "Return to Title", + "x" : -1, + "y" : 515, + "w" : 200, + "h": 34 + } + +] diff --git a/data/widgets/inBattle.json b/data/widgets/inBattle.json new file mode 100644 index 0000000..471540c --- /dev/null +++ b/data/widgets/inBattle.json @@ -0,0 +1,43 @@ +[ + { + "name" : "resume", + "group" : "inBattle", + "type" : "WT_BUTTON", + "text" : "Resume", + "x" : -1, + "y" : 215, + "w" : 200, + "h": 34 + }, + { + "name" : "options", + "group" : "inBattle", + "type" : "WT_BUTTON", + "text" : "Options", + "x" : -1, + "y" : 315, + "w" : 200, + "h": 34 + }, + { + "name" : "restart", + "group" : "inBattle", + "type" : "WT_BUTTON", + "text" : "Restart", + "x" : -1, + "y" : 415, + "w" : 200, + "h": 34 + }, + { + "name" : "quit", + "group" : "inBattle", + "type" : "WT_BUTTON", + "text" : "Quit", + "x" : -1, + "y" : 515, + "w" : 200, + "h": 34 + } + +] diff --git a/data/widgets/list.json b/data/widgets/list.json new file mode 100644 index 0000000..9244008 --- /dev/null +++ b/data/widgets/list.json @@ -0,0 +1,11 @@ +[ + "data/widgets/galacticMap.json", + "data/widgets/okCancel.json", + "data/widgets/startBattle.json", + "data/widgets/inBattle.json", + "data/widgets/battleWon.json", + "data/widgets/battleLost.json", + "data/widgets/title.json", + "data/widgets/options.json", + "data/widgets/stats.json" +] diff --git a/data/widgets/okCancel.json b/data/widgets/okCancel.json new file mode 100644 index 0000000..ea43dc4 --- /dev/null +++ b/data/widgets/okCancel.json @@ -0,0 +1,22 @@ +[ + { + "name" : "ok", + "group" : "okCancel", + "type" : "WT_BUTTON", + "text" : "OK", + "x" : 490, + "y" : 680, + "w" : 150, + "h": 34 + }, + { + "name" : "cancel", + "group" : "okCancel", + "type" : "WT_BUTTON", + "text" : "Cancel", + "x" : 790, + "y" : 680, + "w" : 150, + "h": 34 + } +] diff --git a/data/widgets/options.json b/data/widgets/options.json new file mode 100644 index 0000000..e477a1c --- /dev/null +++ b/data/widgets/options.json @@ -0,0 +1,67 @@ +[ + { + "name" : "windowSize", + "group" : "options", + "type" : "WT_SELECT", + "text" : "Window Size", + "options" : "640 x 360;1280 x 720;1600 x 900;1920 x 1080", + "x" : -1, + "y" : 175, + "w" : 400, + "h": 34 + }, + { + "name" : "soundVolume", + "group" : "options", + "type" : "WT_SELECT", + "text" : "Sound Volume", + "options" : "0;1;2;3;4;5;6;7;8;9;10", + "x" : -1, + "y" : 250, + "w" : 400, + "h": 34 + }, + { + "name" : "musicVolume", + "group" : "options", + "type" : "WT_SELECT", + "text" : "Music Volume", + "options" : "0;1;2;3;4;5;6;7;8;9;10", + "x" : -1, + "y" : 325, + "w" : 400, + "h": 34 + }, + { + "name" : "fullscreen", + "group" : "options", + "type" : "WT_SELECT", + "text" : "Fullscreen", + "options" : "Off;On", + "x" : -1, + "y" : 400, + "w" : 400, + "h": 34 + }, + { + "name" : "vSync", + "group" : "options", + "type" : "WT_SELECT", + "text" : "VSync", + "options" : "Off;On", + "x" : -1, + "y" : 475, + "w" : 400, + "h": 34 + }, + { + "name" : "ok", + "group" : "options", + "type" : "WT_BUTTON", + "text" : "OK", + "x" : -1, + "y" : 625, + "w" : 100, + "h": 34 + } +] diff --git a/data/widgets/startBattle.json b/data/widgets/startBattle.json new file mode 100644 index 0000000..4a5d69d --- /dev/null +++ b/data/widgets/startBattle.json @@ -0,0 +1,12 @@ +[ + { + "name" : "ok", + "group" : "startBattle", + "type" : "WT_BUTTON", + "text" : "OK", + "x" : 640, + "y" : 680, + "w" : 150, + "h": 34 + } +] diff --git a/data/widgets/stats.json b/data/widgets/stats.json new file mode 100644 index 0000000..7744ba5 --- /dev/null +++ b/data/widgets/stats.json @@ -0,0 +1,12 @@ +[ + { + "name" : "ok", + "group" : "stats", + "type" : "WT_BUTTON", + "text" : "OK", + "x" : 640, + "y" : 635, + "w" : 150, + "h": 34 + } +] diff --git a/data/widgets/title.json b/data/widgets/title.json new file mode 100644 index 0000000..1712fd3 --- /dev/null +++ b/data/widgets/title.json @@ -0,0 +1,42 @@ +[ + { + "name" : "newGame", + "group" : "title", + "type" : "WT_BUTTON", + "text" : "New Game", + "x" : -1, + "y" : 250, + "w" : 200, + "h": 34 + }, + { + "name" : "continue", + "group" : "title", + "type" : "WT_BUTTON", + "text" : "Continue", + "x" : -1, + "y" : 350, + "w" : 200, + "h": 34 + }, + { + "name" : "options", + "group" : "title", + "type" : "WT_BUTTON", + "text" : "Options", + "x" : -1, + "y" : 450, + "w" : 200, + "h": 34 + }, + { + "name" : "quit", + "group" : "title", + "type" : "WT_BUTTON", + "text" : "Quit", + "x" : -1, + "y" : 550, + "w" : 200, + "h": 34 + } +] diff --git a/gfx/backgrounds/background01.jpg b/gfx/backgrounds/background01.jpg new file mode 100644 index 0000000..c2a3b0a Binary files /dev/null and b/gfx/backgrounds/background01.jpg differ diff --git a/gfx/backgrounds/background02.jpg b/gfx/backgrounds/background02.jpg new file mode 100644 index 0000000..575ec4d Binary files /dev/null and b/gfx/backgrounds/background02.jpg differ diff --git a/gfx/backgrounds/background03.jpg b/gfx/backgrounds/background03.jpg new file mode 100644 index 0000000..bf74df5 Binary files /dev/null and b/gfx/backgrounds/background03.jpg differ diff --git a/gfx/backgrounds/background04.jpg b/gfx/backgrounds/background04.jpg new file mode 100644 index 0000000..582dae4 Binary files /dev/null and b/gfx/backgrounds/background04.jpg differ diff --git a/gfx/battle/explosion.png b/gfx/battle/explosion.png new file mode 100644 index 0000000..f3bb539 Binary files /dev/null and b/gfx/battle/explosion.png differ diff --git a/gfx/battle/halo.png b/gfx/battle/halo.png new file mode 100644 index 0000000..b40fcfa Binary files /dev/null and b/gfx/battle/halo.png differ diff --git a/gfx/battle/missionComplete.png b/gfx/battle/missionComplete.png new file mode 100644 index 0000000..8a2c4f9 Binary files /dev/null and b/gfx/battle/missionComplete.png differ diff --git a/gfx/battle/missionFailed.png b/gfx/battle/missionFailed.png new file mode 100644 index 0000000..6b4047f Binary files /dev/null and b/gfx/battle/missionFailed.png differ diff --git a/gfx/battle/missionInProgress.png b/gfx/battle/missionInProgress.png new file mode 100644 index 0000000..3939a3e Binary files /dev/null and b/gfx/battle/missionInProgress.png differ diff --git a/gfx/battle/missionStart.png b/gfx/battle/missionStart.png new file mode 100644 index 0000000..70285ba Binary files /dev/null and b/gfx/battle/missionStart.png differ diff --git a/gfx/battle/shieldHit.png b/gfx/battle/shieldHit.png new file mode 100644 index 0000000..9d457aa Binary files /dev/null and b/gfx/battle/shieldHit.png differ diff --git a/gfx/bullets/magBolt.png b/gfx/bullets/magBolt.png new file mode 100644 index 0000000..c037ad0 Binary files /dev/null and b/gfx/bullets/magBolt.png differ diff --git a/gfx/bullets/missile.png b/gfx/bullets/missile.png new file mode 100644 index 0000000..f87ed13 Binary files /dev/null and b/gfx/bullets/missile.png differ diff --git a/gfx/bullets/particleBolt.png b/gfx/bullets/particleBolt.png new file mode 100644 index 0000000..4c0adb5 Binary files /dev/null and b/gfx/bullets/particleBolt.png differ diff --git a/gfx/bullets/plasmaBolt.png b/gfx/bullets/plasmaBolt.png new file mode 100644 index 0000000..6c2b83e Binary files /dev/null and b/gfx/bullets/plasmaBolt.png differ diff --git a/gfx/fighters/acceleratorODP.png b/gfx/fighters/acceleratorODP.png new file mode 100644 index 0000000..59e345c Binary files /dev/null and b/gfx/fighters/acceleratorODP.png differ diff --git a/gfx/fighters/ataf.png b/gfx/fighters/ataf.png new file mode 100644 index 0000000..26c8c21 Binary files /dev/null and b/gfx/fighters/ataf.png differ diff --git a/gfx/fighters/dart01.png b/gfx/fighters/dart01.png new file mode 100644 index 0000000..7e50369 Binary files /dev/null and b/gfx/fighters/dart01.png differ diff --git a/gfx/fighters/dart02.png b/gfx/fighters/dart02.png new file mode 100644 index 0000000..bacd590 Binary files /dev/null and b/gfx/fighters/dart02.png differ diff --git a/gfx/fighters/dart03.png b/gfx/fighters/dart03.png new file mode 100644 index 0000000..3b61fe5 Binary files /dev/null and b/gfx/fighters/dart03.png differ diff --git a/gfx/fighters/dart04.png b/gfx/fighters/dart04.png new file mode 100644 index 0000000..fc5e24a Binary files /dev/null and b/gfx/fighters/dart04.png differ diff --git a/gfx/fighters/dart05.png b/gfx/fighters/dart05.png new file mode 100644 index 0000000..e3ed507 Binary files /dev/null and b/gfx/fighters/dart05.png differ diff --git a/gfx/fighters/dart06.png b/gfx/fighters/dart06.png new file mode 100644 index 0000000..9e54209 Binary files /dev/null and b/gfx/fighters/dart06.png differ diff --git a/gfx/fighters/dart07.png b/gfx/fighters/dart07.png new file mode 100644 index 0000000..6981bb3 Binary files /dev/null and b/gfx/fighters/dart07.png differ diff --git a/gfx/fighters/firefly.png b/gfx/fighters/firefly.png new file mode 100644 index 0000000..edff0ed Binary files /dev/null and b/gfx/fighters/firefly.png differ diff --git a/gfx/fighters/hammerhead.png b/gfx/fighters/hammerhead.png new file mode 100644 index 0000000..9d82b63 Binary files /dev/null and b/gfx/fighters/hammerhead.png differ diff --git a/gfx/fighters/hyena.png b/gfx/fighters/hyena.png new file mode 100644 index 0000000..486c1ab Binary files /dev/null and b/gfx/fighters/hyena.png differ diff --git a/gfx/fighters/jackal.png b/gfx/fighters/jackal.png new file mode 100644 index 0000000..4d865f3 Binary files /dev/null and b/gfx/fighters/jackal.png differ diff --git a/gfx/fighters/khepri.png b/gfx/fighters/khepri.png new file mode 100644 index 0000000..d3494ce Binary files /dev/null and b/gfx/fighters/khepri.png differ diff --git a/gfx/fighters/kingfisher.png b/gfx/fighters/kingfisher.png new file mode 100644 index 0000000..5ff0eac Binary files /dev/null and b/gfx/fighters/kingfisher.png differ diff --git a/gfx/fighters/leopard.png b/gfx/fighters/leopard.png new file mode 100644 index 0000000..463949a Binary files /dev/null and b/gfx/fighters/leopard.png differ diff --git a/gfx/fighters/mantis.png b/gfx/fighters/mantis.png new file mode 100644 index 0000000..cea2961 Binary files /dev/null and b/gfx/fighters/mantis.png differ diff --git a/gfx/fighters/nymph.png b/gfx/fighters/nymph.png new file mode 100644 index 0000000..ba87df4 Binary files /dev/null and b/gfx/fighters/nymph.png differ diff --git a/gfx/fighters/plasmaODP.png b/gfx/fighters/plasmaODP.png new file mode 100644 index 0000000..db9b5ee Binary files /dev/null and b/gfx/fighters/plasmaODP.png differ diff --git a/gfx/fighters/ray.png b/gfx/fighters/ray.png new file mode 100644 index 0000000..eedf5a8 Binary files /dev/null and b/gfx/fighters/ray.png differ diff --git a/gfx/fighters/rocketODP.png b/gfx/fighters/rocketODP.png new file mode 100644 index 0000000..31c729a Binary files /dev/null and b/gfx/fighters/rocketODP.png differ diff --git a/gfx/fighters/rook.png b/gfx/fighters/rook.png new file mode 100644 index 0000000..aa7489a Binary files /dev/null and b/gfx/fighters/rook.png differ diff --git a/gfx/fighters/scarab.png b/gfx/fighters/scarab.png new file mode 100644 index 0000000..f46ba11 Binary files /dev/null and b/gfx/fighters/scarab.png differ diff --git a/gfx/fighters/sphinx.png b/gfx/fighters/sphinx.png new file mode 100644 index 0000000..0c4110e Binary files /dev/null and b/gfx/fighters/sphinx.png differ diff --git a/gfx/fighters/taf.png b/gfx/fighters/taf.png new file mode 100644 index 0000000..55b689a Binary files /dev/null and b/gfx/fighters/taf.png differ diff --git a/gfx/fighters/thunderhead.png b/gfx/fighters/thunderhead.png new file mode 100644 index 0000000..402eab1 Binary files /dev/null and b/gfx/fighters/thunderhead.png differ diff --git a/gfx/galaxy/starSystem.png b/gfx/galaxy/starSystem.png new file mode 100644 index 0000000..44b22bc Binary files /dev/null and b/gfx/galaxy/starSystem.png differ diff --git a/gfx/hud/smallFighter.png b/gfx/hud/smallFighter.png new file mode 100644 index 0000000..4479e43 Binary files /dev/null and b/gfx/hud/smallFighter.png differ diff --git a/gfx/hud/targetCircle.png b/gfx/hud/targetCircle.png new file mode 100644 index 0000000..924ef0e Binary files /dev/null and b/gfx/hud/targetCircle.png differ diff --git a/gfx/hud/targetPointer.png b/gfx/hud/targetPointer.png new file mode 100644 index 0000000..6f6ff7d Binary files /dev/null and b/gfx/hud/targetPointer.png differ diff --git a/gfx/planets/adelaide.png b/gfx/planets/adelaide.png new file mode 100755 index 0000000..117d538 Binary files /dev/null and b/gfx/planets/adelaide.png differ diff --git a/gfx/planets/al-Elfia.png b/gfx/planets/al-Elfia.png new file mode 100755 index 0000000..b37414e Binary files /dev/null and b/gfx/planets/al-Elfia.png differ diff --git a/gfx/planets/arlos.png b/gfx/planets/arlos.png new file mode 100755 index 0000000..32b3ebe Binary files /dev/null and b/gfx/planets/arlos.png differ diff --git a/gfx/planets/bluePlanet.png b/gfx/planets/bluePlanet.png new file mode 100755 index 0000000..b7dfe0e Binary files /dev/null and b/gfx/planets/bluePlanet.png differ diff --git a/gfx/planets/diso.png b/gfx/planets/diso.png new file mode 100755 index 0000000..ae1b988 Binary files /dev/null and b/gfx/planets/diso.png differ diff --git a/gfx/planets/earth.png b/gfx/planets/earth.png new file mode 100755 index 0000000..c8e8c7a Binary files /dev/null and b/gfx/planets/earth.png differ diff --git a/gfx/planets/henninger.png b/gfx/planets/henninger.png new file mode 100755 index 0000000..74546d9 Binary files /dev/null and b/gfx/planets/henninger.png differ diff --git a/gfx/planets/hyanik.png b/gfx/planets/hyanik.png new file mode 100755 index 0000000..10cd014 Binary files /dev/null and b/gfx/planets/hyanik.png differ diff --git a/gfx/planets/kaiser.png b/gfx/planets/kaiser.png new file mode 100755 index 0000000..5616b52 Binary files /dev/null and b/gfx/planets/kaiser.png differ diff --git a/gfx/planets/kethlan.png b/gfx/planets/kethlan.png new file mode 100755 index 0000000..356888e Binary files /dev/null and b/gfx/planets/kethlan.png differ diff --git a/gfx/planets/mythos.png b/gfx/planets/mythos.png new file mode 100755 index 0000000..dbcc133 Binary files /dev/null and b/gfx/planets/mythos.png differ diff --git a/gfx/planets/oracleIX.png b/gfx/planets/oracleIX.png new file mode 100755 index 0000000..d850c2d Binary files /dev/null and b/gfx/planets/oracleIX.png differ diff --git a/gfx/planets/peri.png b/gfx/planets/peri.png new file mode 100755 index 0000000..80abf3c Binary files /dev/null and b/gfx/planets/peri.png differ diff --git a/gfx/planets/sky.png b/gfx/planets/sky.png new file mode 100755 index 0000000..b644898 Binary files /dev/null and b/gfx/planets/sky.png differ diff --git a/gfx/planets/spirit.png b/gfx/planets/spirit.png new file mode 100755 index 0000000..61beb96 Binary files /dev/null and b/gfx/planets/spirit.png differ diff --git a/gfx/planets/tigibel.png b/gfx/planets/tigibel.png new file mode 100755 index 0000000..aa55a2a Binary files /dev/null and b/gfx/planets/tigibel.png differ diff --git a/gfx/planets/tilli.png b/gfx/planets/tilli.png new file mode 100755 index 0000000..df158ce Binary files /dev/null and b/gfx/planets/tilli.png differ diff --git a/gfx/planets/torelli.png b/gfx/planets/torelli.png new file mode 100755 index 0000000..0795b77 Binary files /dev/null and b/gfx/planets/torelli.png differ diff --git a/gfx/planets/xalan.png b/gfx/planets/xalan.png new file mode 100755 index 0000000..9f900fe Binary files /dev/null and b/gfx/planets/xalan.png differ diff --git a/gfx/title/logo.png b/gfx/title/logo.png new file mode 100644 index 0000000..0361b25 Binary files /dev/null and b/gfx/title/logo.png differ diff --git a/gfx/title/pandoran.png b/gfx/title/pandoran.png new file mode 100644 index 0000000..eae30cc Binary files /dev/null and b/gfx/title/pandoran.png differ diff --git a/gfx/widgets/optionsLeft.png b/gfx/widgets/optionsLeft.png new file mode 100644 index 0000000..29da76b Binary files /dev/null and b/gfx/widgets/optionsLeft.png differ diff --git a/gfx/widgets/optionsRight.png b/gfx/widgets/optionsRight.png new file mode 100644 index 0000000..4a0d7fe Binary files /dev/null and b/gfx/widgets/optionsRight.png differ diff --git a/makefile b/makefile new file mode 100644 index 0000000..9325176 --- /dev/null +++ b/makefile @@ -0,0 +1,51 @@ +PROG = tbftss + +VERSION = 0.1 +RELEASE = 1 + +CXXFLAGS += `sdl2-config --cflags` -DVERSION=$(VERSION) -DRELEASE=$(RELEASE) -DUNIX=1 +CXXFLAGS += -DUNIX +CXXFLAGS += $(CFLAGS) -Wall -ansi -pedantic -Werror -Wstrict-prototypes +CXXFLAGS += -g -lefence + +LIBS = `sdl2-config --libs` -lSDL2_mixer -lSDL2_image -lSDL2_ttf -lm + +SEARCHPATH += src/ src/battle src/draw src/game src/galaxy src/json src/system +vpath %.c $(SEARCHPATH) +vpath %.h $(SEARCHPATH) + +DEPS += defs.h structs.h + +OBJS += ai.o +OBJS += battle.o bullets.o +OBJS += challenges.o cJSON.o +OBJS += draw.o +OBJS += effects.o +OBJS += fighters.o fighterDefs.o +OBJS += galacticMap.o game.o +OBJS += hud.o +OBJS += init.o io.o +OBJS += load.o lookup.o +OBJS += main.o mission.o missionInfo.o +OBJS += objectives.o options.o +OBJS += player.o +OBJS += radar.o +OBJS += save.o sound.o starfield.o starSystems.o stats.o +OBJS += textures.o text.o title.o transition.o +OBJS += util.o +OBJS += widgets.o + +# top-level rule to create the program. +all: $(PROG) + +# compiling other source files. +%.o: %.c %.h $(DEPS) + $(CC) $(CXXFLAGS) -c $< + +# linking the program. +$(PROG): $(OBJS) + $(CC) -o $(PROG) $(OBJS) $(LIBS) + +# cleaning everything that can be automatically recreated with "make". +clean: + $(RM) $(OBJS) $(PROG) $(SERVEROBJS) $(SERVER) diff --git a/manual/index.html b/manual/index.html new file mode 100644 index 0000000..e41413f --- /dev/null +++ b/manual/index.html @@ -0,0 +1,64 @@ + + + +

The Battle for the Solar System

+

The Pandoran War

+ +

Introduction

+

+The Battle for the Solar System - The Pandoran War is a 2D space shooter, based on the space opera novels by Stephen J Sweeney. +

+

Note: this game is still under heavy development and will feature bugs, incomplete sections, and a numerous other issues until it reaches v1.0. Likewise, this gameplay manually will evolve over time, to become more detailed and helpful.

+ +

Galactic Map

+

+This is the main mission select screen. Here, you can move around and select star systems in which to undertake missions. Star Systems that have missions will emit red circles at regular intervals. Yellow circles will be emitted by star systems that have no active missions, but still have challenges that can be attempted. +

+ +

Controls

+ + + + +
Arrow keysScroll galactic map
ReturnSelect Star System
EscapeMenu
+ +

Star Systems

+

+The Star System view shows a list of missions available for that star system. Missions must be played sequentially, with the next available mission being highlighted in yellow. The right-hand side of the star system view displays an overview of the mission, including the class of craft the player will be piloting, and the name of the pilot and their squadron. +

+ +

Controls

+ + + + +
Arrow keysScroll galactic map
ReturnSelect Star System
EscapeMenu
+ +

Missions

+

+Each mission has various objectives that the player must finish in order to complete the mission successfully. The mission ends when the player either successfully completes all the objectives, or is killed. +

+ +

Controls

+ + + + + + + + + +
Left arrowRotate anti-clockwise
Right arrowRotate clockwise
Up arrowAccelerate
Down arrowDecelerate
Left controlFire main cannons
ReturnLaunch missile
TabDisplay objectives
EscapeMenu
+ +

About

+

+This game is based on the Battle for the Solar System novel trilogy by Stephen J Sweeney. You can learn more about the trilogy by visiting: +

+ +

www.battleforthesolarsystem.com

+ +

The Battle for the Solar System and related related materials are Copyright ©2009-2015, Stephen J Sweeney. All Rights Reserved.

+ + + diff --git a/music/Battle in the winter.mp3 b/music/Battle in the winter.mp3 new file mode 100644 index 0000000..fcb98c4 Binary files /dev/null and b/music/Battle in the winter.mp3 differ diff --git a/music/Pressure.ogg b/music/Pressure.ogg new file mode 100644 index 0000000..922b0d3 Binary files /dev/null and b/music/Pressure.ogg differ diff --git a/music/Rise of spirit.ogg b/music/Rise of spirit.ogg new file mode 100644 index 0000000..c31277c Binary files /dev/null and b/music/Rise of spirit.ogg differ diff --git a/music/Showdown.mp3 b/music/Showdown.mp3 new file mode 100644 index 0000000..a8164bf Binary files /dev/null and b/music/Showdown.mp3 differ diff --git a/music/battleThemeA.mp3 b/music/battleThemeA.mp3 new file mode 100644 index 0000000..d22aa61 Binary files /dev/null and b/music/battleThemeA.mp3 differ diff --git a/music/determination.mp3 b/music/determination.mp3 new file mode 100644 index 0000000..0449c63 Binary files /dev/null and b/music/determination.mp3 differ diff --git a/music/heroism.ogg b/music/heroism.ogg new file mode 100644 index 0000000..279c48c Binary files /dev/null and b/music/heroism.ogg differ diff --git a/sound/162265__qubodup__explosive.ogg b/sound/162265__qubodup__explosive.ogg new file mode 100644 index 0000000..3126c69 Binary files /dev/null and b/sound/162265__qubodup__explosive.ogg differ diff --git a/sound/178064__jorickhoofd__slam-door-shut.ogg b/sound/178064__jorickhoofd__slam-door-shut.ogg new file mode 100644 index 0000000..df74680 Binary files /dev/null and b/sound/178064__jorickhoofd__slam-door-shut.ogg differ diff --git a/sound/207322__animationisaac__short-explosion.ogg b/sound/207322__animationisaac__short-explosion.ogg new file mode 100644 index 0000000..55a756f Binary files /dev/null and b/sound/207322__animationisaac__short-explosion.ogg differ diff --git a/sound/249300__suntemple__access-denied.ogg b/sound/249300__suntemple__access-denied.ogg new file mode 100644 index 0000000..0460b8c Binary files /dev/null and b/sound/249300__suntemple__access-denied.ogg differ diff --git a/sound/254071__tb0y298__firework-explosion.ogg b/sound/254071__tb0y298__firework-explosion.ogg new file mode 100644 index 0000000..3570832 Binary files /dev/null and b/sound/254071__tb0y298__firework-explosion.ogg differ diff --git a/sound/257786__xtrgamr__mouse-click.ogg b/sound/257786__xtrgamr__mouse-click.ogg new file mode 100644 index 0000000..66cd162 Binary files /dev/null and b/sound/257786__xtrgamr__mouse-click.ogg differ diff --git a/sound/263621__jamesabdulrahman__permission-to-panic.ogg b/sound/263621__jamesabdulrahman__permission-to-panic.ogg new file mode 100644 index 0000000..22a380b Binary files /dev/null and b/sound/263621__jamesabdulrahman__permission-to-panic.ogg differ diff --git a/sound/268344__julien-matthey__jm-noiz-laser-01.ogg b/sound/268344__julien-matthey__jm-noiz-laser-01.ogg new file mode 100644 index 0000000..46bd87d Binary files /dev/null and b/sound/268344__julien-matthey__jm-noiz-laser-01.ogg differ diff --git a/sound/275151__bird-man__gun-shot.ogg b/sound/275151__bird-man__gun-shot.ogg new file mode 100644 index 0000000..687c660 Binary files /dev/null and b/sound/275151__bird-man__gun-shot.ogg differ diff --git a/sound/321104__nsstudios__blip2.ogg b/sound/321104__nsstudios__blip2.ogg new file mode 100644 index 0000000..acd92eb Binary files /dev/null and b/sound/321104__nsstudios__blip2.ogg differ diff --git a/sound/35677__jobro__laser1.ogg b/sound/35677__jobro__laser1.ogg new file mode 100644 index 0000000..1f7bf2b Binary files /dev/null and b/sound/35677__jobro__laser1.ogg differ diff --git a/sound/42106__marcuslee__laser-wrath-4.ogg b/sound/42106__marcuslee__laser-wrath-4.ogg new file mode 100644 index 0000000..d2cde08 Binary files /dev/null and b/sound/42106__marcuslee__laser-wrath-4.ogg differ diff --git a/sound/47252__nthompson__bad-explosion.ogg b/sound/47252__nthompson__bad-explosion.ogg new file mode 100644 index 0000000..0762ece Binary files /dev/null and b/sound/47252__nthompson__bad-explosion.ogg differ diff --git a/sound/77087__supraliminal__laser-short.ogg b/sound/77087__supraliminal__laser-short.ogg new file mode 100644 index 0000000..6e554d8 Binary files /dev/null and b/sound/77087__supraliminal__laser-short.ogg differ diff --git a/src/battle/ai.c b/src/battle/ai.c new file mode 100644 index 0000000..bb5111b --- /dev/null +++ b/src/battle/ai.c @@ -0,0 +1,293 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "ai.h" + +static int aggression[][5] = +{ + {25, 35, 35, 40, 50}, + {20, 30, 30, 35, 40}, + {15, 20, 25, 30, 35}, + {10, 15, 20, 25, 30}, + {5, 10, 15, 20, 25} +}; + +static void faceTarget(Fighter *f); +static int isInFOV(Fighter *f, int fov); +static void preAttack(void); +static void huntTarget(void); +static void huntAndAttackTarget(void); +static void flyStraight(void); +static void dodge(void); +static void nextAction(void); +static void findTarget(void); +static int hasClearShot(void); +static void boost(void); +static void slow(void); +static int targetOutOfRange(void); +static void moveToPlayer(void); + +void doAI(void) +{ + int r; + + if (!self->target || targetOutOfRange()) + { + findTarget(); + + if (self->target == NULL) + { + if (player != NULL && self->side == player->side) + { + moveToPlayer(); + } + else + { + applyFighterBrakes(); + } + return; + } + } + + r = rand() % 100; + + if (r <= aggression[self->aggression][0]) + { + self->action = dodge; + self->aiActionTime = FPS * 2; + } + else if (r <= aggression[self->aggression][1]) + { + self->action = boost; + self->aiActionTime = FPS * 1; + } + else if (r <= aggression[self->aggression][2]) + { + self->action = slow; + self->aiActionTime = FPS * 1; + } + else if (r <= aggression[self->aggression][3]) + { + self->action = flyStraight; + self->aiActionTime = FPS * 1; + } + else if (r <= aggression[self->aggression][4]) + { + self->action = huntTarget; + self->aiActionTime = FPS * 2; + } + else + { + self->action = huntAndAttackTarget; + self->aiActionTime = FPS * 1; + } +} + +static int targetOutOfRange(void) +{ + return getDistance(self->x, self->y, self->target->x, self->target->y) > 2000; +} + +static void huntTarget(void) +{ + faceTarget(self->target); + + applyFighterThrust(); + + nextAction(); +} + +static void huntAndAttackTarget(void) +{ + int dist = getDistance(self->x, self->y, self->target->x, self->target->y); + + faceTarget(self->target); + + if (dist <= 500 && hasClearShot()) + { + preAttack(); + } + + if (dist <= 250) + { + applyFighterBrakes(); + self->aiActionTime = MIN(FPS, self->aiActionTime); + } + else + { + applyFighterThrust(); + } + + nextAction(); +} + +static void findTarget(void) +{ + Fighter *f; + int closest = 2000; + int dist = 2000; + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + if (f->side != self->side && f->health > 0) + { + dist = getDistance(self->x, self->y, f->x, f->y); + if (dist < closest) + { + self->target = f; + closest = dist; + } + } + } +} + +static void faceTarget(Fighter *f) +{ + int dir; + int wantedAngle = getAngle(self->x, self->y, f->x, f->y); + + wantedAngle %= 360; + + if (fabs(wantedAngle - self->angle) > TURN_THRESHOLD) + { + dir = (wantedAngle - self->angle + 360) % 360 > 180 ? -1 : 1; + + self->angle += dir * TURN_SPEED; + + self->angle = mod(self->angle, 360); + + applyFighterBrakes(); + } +} + +static int isInFOV(Fighter *f, int fov) +{ + int angle, a, b; + + a = mod(self->angle - fov, 360); + b = mod(self->angle + fov, 360); + angle = getAngle(self->x, self->y, f->x, f->y); + + return (a < b) ? (a <= angle && angle <= b) : (a <= angle || angle <= b); +} + +static void boost(void) +{ + self->dx *= 1.001; + self->dy *= 1.001; + + nextAction(); +} + +static void slow(void) +{ + self->dx *= 0.95; + self->dy *= 0.95; + + nextAction(); +} + +static int hasClearShot(void) +{ + int dist; + Fighter *f; + + if (isInFOV(self->target, 4)) + { + dist = getDistance(self->x, self->y, self->target->x, self->target->y); + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + if (f != self && f != self->target && (getDistance(self->x, self->y, f->x, f->y) < dist)) + { + if (isInFOV(f, 8)) + { + return 0; + } + } + } + + return 1; + } + + return 0; +} + +static void preAttack(void) +{ + if (!self->reload && self->guns[0].type) + { + fireGuns(self); + } +} + +static void flyStraight(void) +{ + applyFighterThrust(); + + nextAction(); +} + +static void dodge(void) +{ + int dir; + int wantedAngle = 180 + getAngle(self->x, self->y, self->target->x, self->target->y); + + wantedAngle %= 360; + + if (fabs(wantedAngle - self->angle) > TURN_THRESHOLD) + { + dir = (wantedAngle - self->angle + 360) % 360 > 180 ? -1 : 1; + + self->angle += dir * TURN_SPEED; + + self->angle = mod(self->angle, 360); + } + + applyFighterThrust(); + + nextAction(); +} + +static void nextAction(void) +{ + if (--self->aiActionTime <= 0) + { + self->action = doAI; + } +} + +static void moveToPlayer(void) +{ + int dist = getDistance(self->x, self->y, player->x, player->y); + + if (dist <= 250) + { + applyFighterBrakes(); + + self->aiActionTime = MIN(FPS, self->aiActionTime); + } + else + { + faceTarget(player); + + applyFighterThrust(); + } +} diff --git a/src/battle/ai.h b/src/battle/ai.h new file mode 100644 index 0000000..b606de0 --- /dev/null +++ b/src/battle/ai.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +#define TURN_SPEED 4 +#define TURN_THRESHOLD 2 + +extern int mod(int n, int x); +extern int getDistance(int x1, int y1, int x2, int y2); +extern void fireGuns(Fighter *owner); +extern float getAngle(int x1, int y1, int x2, int y2); +extern void applyFighterThrust(void); +extern void applyFighterBrakes(void); + +extern Battle battle; +extern Fighter *self; +extern Fighter *player; diff --git a/src/battle/battle.c b/src/battle/battle.c new file mode 100644 index 0000000..bf623ef --- /dev/null +++ b/src/battle/battle.c @@ -0,0 +1,355 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "battle.h" + +static void logic(void); +static void draw(void); +static void handleKeyboard(void); +static void postBattle(void); +void destroyBattle(void); +static void doBattle(void); +static void quitBattle(void); +static void drawMenu(void); +static void continueGame(void); +static void resume(void); +static void retry(void); +static void start(void); +static void options(void); +static void returnFromOptions(void); + +static int show; + +void initBattle(void) +{ + memset(&battle, 0, sizeof(Battle)); + battle.bulletTail = &battle.bulletHead; + battle.fighterTail = &battle.fighterHead; + battle.effectTail = &battle.effectHead; + battle.objectiveTail = &battle.objectiveHead; + + app.delegate.logic = &logic; + app.delegate.draw = &draw; + memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + initStars(); + + initBackground(); + + initHud(); + + initMissionInfo(); + + show = SHOW_BATTLE; + + getWidget("ok", "startBattle")->action = start; + + getWidget("resume", "inBattle")->action = resume; + getWidget("options", "inBattle")->action = options; + getWidget("restart", "inBattle")->action = retry; + getWidget("quit", "inBattle")->action = quitBattle; + + getWidget("continue", "battleWon")->action = continueGame; + getWidget("retry", "battleWon")->action = retry; + + getWidget("retry", "battleLost")->action = retry; + getWidget("quit", "battleLost")->action = quitBattle; + + selectWidget("ok", "startBattle"); + + /* only increment the missions started if there are objectives (Free Flight) */ + if (battle.objectiveHead.next) + { + game.stats.missionsStarted++; + } +} + +static void logic(void) +{ + if (battle.status == MS_IN_PROGRESS || battle.status == MS_COMPLETE || battle.status == MS_FAILED) + { + handleKeyboard(); + + if (show == SHOW_BATTLE) + { + doBattle(); + } + } + + doWidgets(); +} + +static void doBattle(void) +{ + scrollBackground(-battle.ssx * 0.1, -battle.ssy * 0.1); + + doHud(); + + doObjectives(); + + if (player != NULL) + { + battle.ssx = player->dx; + battle.ssy = player->dy; + } + else + { + battle.ssx *= 0.99; + battle.ssy *= 0.99; + } + + battle.planet.x -= (battle.ssx * 0.25); + battle.planet.y -= (battle.ssy * 0.25); + + doStars(battle.ssx, battle.ssy); + + doBullets(); + + doFighters(); + + doEffects(); + + doPlayer(); + + if (battle.status != MS_IN_PROGRESS) + { + battle.missionFinishedTimer--; + + if (battle.missionFinishedTimer == 0) + { + if (battle.status == MS_COMPLETE) + { + selectWidget("continue", "battleWon"); + } + else + { + selectWidget("retry", "battleLost"); + } + } + } +} + +static void draw(void) +{ + prepareScene(); + + drawBackground(battle.background); + + drawStars(); + + blit(battle.planetTexture, battle.planet.x, battle.planet.y, 1); + + drawBullets(); + + drawFighters(); + + drawEffects(); + + drawHud(); + + drawMissionInfo(); + + switch (show) + { + case SHOW_MENU: + drawMenu(); + break; + + case SHOW_OPTIONS: + drawOptions(); + break; + } + + presentScene(); +} + +static void drawMenu(void) +{ + SDL_Rect r; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); + SDL_RenderFillRect(app.renderer, NULL); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + r.w = 400; + r.h = 400; + r.x = (SCREEN_WIDTH / 2) - r.w / 2; + r.y = (SCREEN_HEIGHT / 2) - r.h / 2; + + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255); + SDL_RenderDrawRect(app.renderer, &r); + + drawWidgets("inBattle"); +} + +static void handleKeyboard(void) +{ + if (app.keyboard[SDL_SCANCODE_ESCAPE]) + { + switch (show) + { + case SHOW_BATTLE: + case SHOW_OPTIONS: + selectWidget("resume", "inBattle"); + show = SHOW_MENU; + break; + + case SHOW_MENU: + show = SHOW_BATTLE; + break; + + case SHOW_OBJECTIVES: + show = SHOW_BATTLE; + break; + } + + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + playSound(SND_GUI_CLOSE); + } + + if (app.keyboard[SDL_SCANCODE_TAB]) + { + battle.status = MS_PAUSED; + } +} + +static void start(void) +{ + battle.status = MS_IN_PROGRESS; +} + +static void resume(void) +{ + show = SHOW_BATTLE; + + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); +} + +static void continueGame(void) +{ + postBattle(); + + destroyBattle(); + + resetHud(); + + initGalacticMap(); +} + +static void options(void) +{ + show = SHOW_OPTIONS; + + initOptions(returnFromOptions); +} + +static void returnFromOptions(void) +{ + show = SHOW_MENU; + + selectWidget("resume", "inBattle"); +} + +static void retry(void) +{ + postBattle(); + + destroyBattle(); + + resetHud(); + + initBattle(); + + loadMission(game.currentMission->filename); +} + +static void quitBattle(void) +{ + postBattle(); + + destroyBattle(); + + resetHud(); + + initGalacticMap(); +} + +static void postBattle(void) +{ + game.stats.shotsFired += battle.stats.shotsFired; + game.stats.shotsHit += battle.stats.shotsHit; + game.stats.missilesFired += battle.stats.missilesFired; + game.stats.missilesHit += battle.stats.missilesHit; + game.stats.enemiesKilled += battle.stats.enemiesKilled; + game.stats.alliesKilled += battle.stats.alliesKilled; + game.stats.playerKilled += battle.stats.playerKilled; + game.stats.playerKills += battle.stats.playerKills; + game.stats.time += battle.stats.time; + + if (game.currentMission && !game.currentMission->completed) + { + game.currentMission->completed = (battle.status == MS_COMPLETE || !battle.numObjectivesTotal); + } + +} + +void destroyBattle(void) +{ + Fighter *f; + Bullet *b; + Effect *e; + Objective *o; + + while (battle.fighterHead.next) + { + f = battle.fighterHead.next; + battle.fighterHead.next = f->next; + free(f); + } + battle.fighterTail = &battle.fighterHead; + + while (battle.bulletHead.next) + { + b = battle.bulletHead.next; + battle.bulletHead.next = b->next; + free(b); + } + battle.bulletTail = &battle.bulletHead; + + while (battle.effectHead.next) + { + e = battle.effectHead.next; + battle.effectHead.next = e->next; + free(e); + } + battle.effectTail = &battle.effectHead; + + while (battle.objectiveHead.next) + { + o = battle.objectiveHead.next; + battle.objectiveHead.next = o->next; + free(o); + } + battle.objectiveTail = &battle.objectiveHead; +} diff --git a/src/battle/battle.h b/src/battle/battle.h new file mode 100644 index 0000000..200f220 --- /dev/null +++ b/src/battle/battle.h @@ -0,0 +1,69 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +#define SHOW_BATTLE 0 +#define SHOW_MENU 1 +#define SHOW_OBJECTIVES 2 +#define SHOW_OPTIONS 3 + +extern void prepareScene(void); +extern void presentScene(void); +extern void doBullets(void); +extern void drawBullets(void); +extern void doStars(float dx, float dy); +extern void drawStars(void); +extern void doFighters(void); +extern void drawFighters(void); +extern void initStars(void); +extern void doPlayer(void); +extern void drawHud(void); +extern void drawEffects(void); +extern void doEffects(void); +extern void doObjectives(void); +extern void blit(SDL_Texture *texture, int x, int y, int centered); +extern void initHud(void); +extern void initGalacticMap(void); +extern void drawWidgets(char *groupName); +extern void selectWidget(const char *name, const char *group); +extern Widget *getWidget(const char *name, const char *group); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern void doWidgets(void); +extern void loadMission(char *filename); +extern void resetHud(void); +extern void doHud(void); +extern void initMissionInfo(void); +extern void drawMissionInfo(void); +extern void drawBackground(SDL_Texture *texture); +extern void initBackground(void); +extern void scrollBackground(float x, float y); +extern void initOptions(void (*returnFromOptions)(void)); +extern void drawOptions(void); +extern void playSound(int id); + +extern App app; +extern Battle battle; +extern Colors colors; +extern Fighter *player; +extern Game game; diff --git a/src/battle/bullets.c b/src/battle/bullets.c new file mode 100644 index 0000000..4528051 --- /dev/null +++ b/src/battle/bullets.c @@ -0,0 +1,271 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "bullets.h" + +static void huntTarget(Bullet *b); +static void checkCollisions(Bullet *b); + +static Bullet bulletDef[BT_MAX]; + +void initBulletDefs(void) +{ + cJSON *root, *node; + char *text; + int type; + Bullet *def; + + memset(&bulletDef, 0, sizeof(Bullet) * BT_MAX); + + text = readFile("data/battle/bullets.json"); + + root = cJSON_Parse(text); + + for (node = root->child ; node != NULL ; node = node->next) + { + type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + + def = &bulletDef[type]; + def->type = type; + def->damage = cJSON_GetObjectItem(node, "damage")->valueint; + def->texture = getTexture(cJSON_GetObjectItem(node, "textureName")->valuestring); + def->sound = lookup(cJSON_GetObjectItem(node, "sound")->valuestring); + def->flags = flagsToLong(cJSON_GetObjectItem(node, "flags")->valuestring); + } + + cJSON_Delete(root); + free(text); +} + +void doBullets(void) +{ + Bullet *b; + Bullet *prev = &battle.bulletHead; + + for (b = battle.bulletHead.next ; b != NULL ; b = b->next) + { + b->x += b->dx; + b->y += b->dy; + + b->x -= battle.ssx; + b->y -= battle.ssy; + + if (b->type == BT_MISSILE) + { + addMissileEngineEffect(b); + + huntTarget(b); + } + + checkCollisions(b); + + if (--b->life <= 0) + { + if (b == battle.bulletTail) + { + battle.bulletTail = prev; + } + + prev->next = b->next; + free(b); + b = prev; + } + + prev = b; + } +} + +static void checkCollisions(Bullet *b) +{ + Fighter *f; + int bw, bh, ew, eh; + + SDL_QueryTexture(b->texture, NULL, NULL, &bw, &bh); + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + SDL_QueryTexture(f->texture, NULL, NULL, &ew, &eh); + + if (b->owner != f && f->health > 0 && collision(b->x - bw / 2, b->y - bh / 2, bw, bh, f->x - ew / 2, f->y - eh / 2, ew, eh)) + { + if (b->owner->side == f->side) + { + b->damage = 0; + } + else if (b->owner == player) + { + battle.stats.shotsHit++; + } + + damageFighter(f, b->damage); + b->life = 0; + + /* assuming that health <= 0 will always mean killed */ + if (f->health <= 0 && b->owner == player) + { + battle.stats.playerKills++; + } + + return; + } + } +} + +void drawBullets(void) +{ + Bullet *b; + + for (b = battle.bulletHead.next ; b != NULL ; b = b->next) + { + blitRotated(b->texture, b->x, b->y, b->angle); + } +} + +static void faceTarget(Bullet *b) +{ + int dir; + int wantedAngle = getAngle(b->x, b->y, b->target->x, b->target->y); + + wantedAngle %= 360; + + if (fabs(wantedAngle - b->angle) > TURN_THRESHOLD) + { + dir = (wantedAngle - b->angle + 360) % 360 > 180 ? -1 : 1; + + b->angle += dir * TURN_SPEED; + + b->angle = mod(b->angle, 360); + + b->dx *= 0.35; + b->dy *= 0.35; + } + else + { + b->angle = wantedAngle; + } +} + +static void applyMissileThrust(Bullet *b) +{ + int maxSpeed; + float v, thrust; + + b->dx += sin(TO_RAIDANS(b->angle)) * 0.5; + b->dy += -cos(TO_RAIDANS(b->angle)) * 0.5; + + maxSpeed = MAX(MIN(b->target->speed + 1, 999), 3); + + thrust = sqrt((b->dx * b->dx) + (b->dy * b->dy)); + + if (thrust > maxSpeed) + { + v = (maxSpeed / sqrt(thrust)); + b->dx = v * b->dx; + b->dy = v * b->dy; + } +} + +static void huntTarget(Bullet *b) +{ + if (b->target != NULL && b->target->health > 0) + { + faceTarget(b); + + applyMissileThrust(b); + } + else + { + b->target = b->owner->target; + } +} + +Bullet *createBullet(int type, int x, int y, Fighter *owner) +{ + Bullet *b; + + b = malloc(sizeof(Bullet)); + memset(b, 0, sizeof(Bullet)); + battle.bulletTail->next = b; + battle.bulletTail = b; + + memcpy(b, &bulletDef[type], sizeof(Bullet)); + + b->next = NULL; + + b->x = x; + b->y = y; + b->dx += sin(TO_RAIDANS(owner->angle)) * 16; + b->dy += -cos(TO_RAIDANS(owner->angle)) * 16; + b->life = FPS * 2; + b->angle = owner->angle; + b->owner = owner; + b->target = owner->target; + + return b; +} + +void fireGuns(Fighter *owner) +{ + Bullet *b; + int i; + float x, y; + float c, s; + + for (i = 0 ; i < MAX_FIGHTER_GUNS ; i++) + { + if (owner->guns[i].type) + { + s = sin(TO_RAIDANS(owner->angle)); + c = cos(TO_RAIDANS(owner->angle)); + + x = (owner->guns[i].x * c) - (owner->guns[i].y * s); + y = (owner->guns[i].x * s) + (owner->guns[i].y * c); + + x += owner->x; + y += owner->y; + + b = createBullet(owner->guns[i].type, x, y, owner); + + if (owner == player) + { + battle.stats.shotsFired++; + } + } + } + + owner->reload = owner->reloadTime; + + playBattleSound(b->sound, owner->x, owner->y); +} + +void fireMissile(Fighter *owner) +{ + Bullet *b; + + b = createBullet(BT_ROCKET + owner->missiles.type, owner->x, owner->y, owner); + + b->life = FPS * 30; + + owner->missiles.ammo--; +} + +void destroyBulletDefs(void) +{ +} diff --git a/src/battle/bullets.h b/src/battle/bullets.h new file mode 100644 index 0000000..7b10524 --- /dev/null +++ b/src/battle/bullets.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +#define TURN_SPEED 2 +#define TURN_THRESHOLD 8 + +extern SDL_Texture *getTexture(char *filename); +extern void blitRotated(SDL_Texture *texture, int x, int y, int angle); +extern int collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); +extern void damageFighter(Fighter *f, int damage); +extern void playBattleSound(int id, int x, int y); +extern long flagsToLong(char *flags); +extern long lookup(char *name); +extern char *readFile(char *filename); +extern float getAngle(int x1, int y1, int x2, int y2); +extern void addMissileEngineEffect(Bullet *b); +extern int mod(int n, int x); + +extern Battle battle; +extern Fighter *player; diff --git a/src/battle/challenges.c b/src/battle/challenges.c new file mode 100644 index 0000000..1ce0055 --- /dev/null +++ b/src/battle/challenges.c @@ -0,0 +1,162 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "challenges.h" + +static void updateTimeChallenge(Challenge *c); +static void updateAccuracyChallenge(Challenge *c); +static void updateArmourChallenge(Challenge *c); +static void updateLossesChallenge(Challenge *c); +static void updatePlayerKillsChallenge(Challenge *c); +static char *getFormattedChallengeDescription(const char *format, ...); +char *getChallengeDescription(Challenge *c); + +static char descriptionBuffer[MAX_DESCRIPTION_LENGTH]; + +static char *challengeDescription[] = { + "Retain at least %d%% armour", + "Finish mission in %d seconds or less", + "Attain a %d%% hit accuracy", + "Do not lose any team mates", + "Do not lose more than 1 team mate", + "Do not lose more than %d team mates", + "Take down %d enemy targets" +}; + +void updateChallenges(void) +{ + Challenge *c; + + for (c = game.currentMission->challengeHead.next ; c != NULL ; c = c->next) + { + if (!c->passed) + { + switch (c->type) + { + case CHALLENGE_TIME: + updateTimeChallenge(c); + break; + + case CHALLENGE_ACCURACY: + updateAccuracyChallenge(c); + break; + + case CHALLENGE_ARMOUR: + updateArmourChallenge(c); + break; + + case CHALLENGE_NO_LOSSES: + case CHALLENGE_1_LOSS: + case CHALLENGE_LOSSES: + updateLossesChallenge(c); + break; + + case CHALLENGE_PLAYER_KILLS: + updatePlayerKillsChallenge(c); + break; + } + } + } +} + +static void updateTimeChallenge(Challenge *c) +{ + if (battle.stats.time / FPS <= c->targetValue) + { + c->passed = 1; + } +} + +static void updateAccuracyChallenge(Challenge *c) +{ + float percent; + + percent = battle.stats.shotsHit; + percent /= battle.stats.shotsFired; + percent *= 100; + + if (percent >= c->targetValue) + { + c->passed = 1; + } +} + +static void updateArmourChallenge(Challenge *c) +{ + float percent; + + percent = player->health; + percent /= player->maxHealth; + percent *= 100; + + if (percent >= c->targetValue) + { + c->passed = 1; + } +} + +static void updateLossesChallenge(Challenge *c) +{ + if (!c->passed) + { + c->passed = battle.stats.alliesKilled <= c->targetValue; + } +} + +static void updatePlayerKillsChallenge(Challenge *c) +{ + if (!c->passed) + { + c->passed = battle.stats.playerKills >= c->targetValue; + } +} + +char *getChallengeDescription(Challenge *c) +{ + return getFormattedChallengeDescription(challengeDescription[c->type], c->targetValue); +} + +Challenge *getChallenge(Mission *mission, int type) +{ + Challenge *challenge; + + for (challenge = mission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + if (challenge->type == type) + { + return challenge; + } + } + + return NULL; +} + +static char *getFormattedChallengeDescription(const char *format, ...) +{ + va_list args; + + memset(&descriptionBuffer, '\0', sizeof(descriptionBuffer)); + + va_start(args, format); + vsprintf(descriptionBuffer, format, args); + va_end(args); + + return descriptionBuffer; +} diff --git a/src/battle/challenges.h b/src/battle/challenges.h new file mode 100644 index 0000000..d30318c --- /dev/null +++ b/src/battle/challenges.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern Battle battle; +extern Fighter *player; +extern Game game; diff --git a/src/battle/effects.c b/src/battle/effects.c new file mode 100644 index 0000000..b3f2015 --- /dev/null +++ b/src/battle/effects.c @@ -0,0 +1,256 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "effects.h" + +static void setRandomFlameHue(Effect *e); + +void doEffects(void) +{ + Effect *e; + Effect *prev = &battle.effectHead; + + for (e = battle.effectHead.next ; e != NULL ; e = e->next) + { + e->x += e->dx; + e->y += e->dy; + + e->x -= battle.ssx; + e->y -= battle.ssy; + + e->health--; + + if (e->health <= 0) + { + if (--e->a <= 0) + { + if (e == battle.effectTail) + { + battle.effectTail = prev; + } + + prev->next = e->next; + free(e); + e = prev; + } + } + + prev = e; + } +} + +void drawEffects(void) +{ + Effect *e; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + + for (e = battle.effectHead.next ; e != NULL ; e = e->next) + { + SDL_SetRenderDrawColor(app.renderer, e->r, e->g, e->b, e->a); + + SDL_SetTextureBlendMode(e->texture, SDL_BLENDMODE_ADD); + SDL_SetTextureAlphaMod(e->texture, e->a); + + switch (e->type) + { + case EFFECT_LINE: + SDL_RenderDrawLine(app.renderer, e->x, e->y, e->x + (e->dx * 3), e->y + (e->dy * 3)); + break; + + case EFFECT_TEXTURE: + SDL_SetTextureColorMod(e->texture, e->r, e->g, e->b); + blitScaled(e->texture, e->x, e->y, e->size, e->size); + break; + + case EFFECT_HALO: + break; + } + } + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); +} + +void addSmallFighterExplosion(void) +{ + Effect *e; + SDL_Texture *t = getTexture("gfx/battle/explosion.png"); + + e = malloc(sizeof(Effect)); + memset(e, 0, sizeof(Effect)); + battle.effectTail->next = e; + battle.effectTail = e; + + e->type = EFFECT_TEXTURE; + + e->x = self->x + (rand() % 16 - rand() % 16); + e->y = self->y + (rand() % 16 - rand() % 16); + e->texture = t; + e->health = 0; + e->size = 32; + + setRandomFlameHue(e); + + e->a = 128 + (rand() % 128); + + e->x -= e->size / 2; + e->y -= e->size / 2; +} + +void addFighterExplosion(void) +{ + int i; + Effect *e; + SDL_Texture *t = getTexture("gfx/battle/explosion.png"); + + for (i = 0 ; i < 32 ; i++) + { + e = malloc(sizeof(Effect)); + memset(e, 0, sizeof(Effect)); + battle.effectTail->next = e; + battle.effectTail = e; + + e->type = EFFECT_TEXTURE; + + e->x = self->x; + e->y = self->y; + e->dx = (rand() % 25) - (rand() % 25); + e->dx *= 0.025; + e->dy = (rand() % 25) - (rand() % 25); + e->dy *= 0.025; + e->texture = t; + e->health = 0; + e->size = 32 + (rand() % 64); + e->r = 255; + + setRandomFlameHue(e); + + e->a = 128 + (rand() % 128); + + e->x -= e->size / 2; + e->y -= e->size / 2; + } + + for (i = 0 ; i < 96 ; i++) + { + e = malloc(sizeof(Effect)); + memset(e, 0, sizeof(Effect)); + battle.effectTail->next = e; + battle.effectTail = e; + + e->type = EFFECT_LINE; + e->x = self->x; + e->y = self->y; + e->dx = rand() % 64 - rand() % 64; + e->dx *= 0.1; + e->dy = rand() % 64 - rand() % 64; + e->dy *= 0.1; + e->health = FPS / 2; + + e->a = 128; + + setRandomFlameHue(e); + } +} + +static void setRandomFlameHue(Effect *e) +{ + e->r = 255; + + switch (rand() % 4) + { + case 0: + break; + + case 1: + e->g = 128; + break; + + case 2: + e->g = 255; + break; + + case 3: + e->g = e->b = 255; + break; + } +} + +void addEngineEffect(void) +{ + Effect *e; + + e = malloc(sizeof(Effect)); + memset(e, 0, sizeof(Effect)); + battle.effectTail->next = e; + battle.effectTail = e; + + e->type = EFFECT_TEXTURE; + + e->x = self->x; + e->y = self->y; + + e->x -= sin(TO_RAIDANS(self->angle)) * 16; + e->y -= -cos(TO_RAIDANS(self->angle)) * 16; + + e->x += rand() % 4; + e->x -= rand() % 4; + + e->texture = getTexture("gfx/battle/explosion.png"); + e->health = 0; + e->size = 16; + e->r = 128; + e->g = 128; + e->b = 255; + e->a = 64; + + e->x -= e->size / 2; + e->y -= e->size / 2; +} + +void addMissileEngineEffect(Bullet *b) +{ + Effect *e; + + e = malloc(sizeof(Effect)); + memset(e, 0, sizeof(Effect)); + battle.effectTail->next = e; + battle.effectTail = e; + + e->type = EFFECT_TEXTURE; + + e->x = b->x; + e->y = b->y; + + e->x -= sin(TO_RAIDANS(b->angle)) * 10; + e->y -= -cos(TO_RAIDANS(b->angle)) * 10; + + e->x += rand() % 4; + e->x -= rand() % 4; + + e->texture = getTexture("gfx/battle/explosion.png"); + e->health = 0; + e->size = 12; + setRandomFlameHue(e); + e->a = 128; + + e->x -= e->size / 2; + e->y -= e->size / 2; +} diff --git a/src/battle/effects.h b/src/battle/effects.h new file mode 100644 index 0000000..e2d738c --- /dev/null +++ b/src/battle/effects.h @@ -0,0 +1,31 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void blitScaled(SDL_Texture *texture, int x, int y, int w, int h); +extern SDL_Texture *getTexture(char *name); + +extern App app; +extern Battle battle; +extern Fighter *self; diff --git a/src/battle/fighterDefs.c b/src/battle/fighterDefs.c new file mode 100644 index 0000000..7bd8402 --- /dev/null +++ b/src/battle/fighterDefs.c @@ -0,0 +1,135 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "fighterDefs.h" + +static void loadFighterDef(char *filename); + +static Fighter defHead, *defTail; + +Fighter *getFighterDef(char *name) +{ + Fighter *f; + + for (f = defHead.next ; f != NULL ; f = f->next) + { + if (strcmp(f->name, name) == 0) + { + return f; + } + } + + printf("Error: no such fighter '%s'\n", name); + exit(1); +} + +void loadFighterDefs(void) +{ + cJSON *root, *node; + char *text; + + text = readFile("data/fighters/list.json"); + root = cJSON_Parse(text); + + memset(&defHead, 0, sizeof(Fighter)); + defTail = &defHead; + + for (node = root->child ; node != NULL ; node = node->next) + { + loadFighterDef(node->valuestring); + } + + cJSON_Delete(root); + free(text); +} + +static void loadFighterDef(char *filename) +{ + cJSON *root, *node; + char *text; + Fighter *f; + int i, w, h; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", filename); + + text = readFile(filename); + + f = malloc(sizeof(Fighter)); + memset(f, 0, sizeof(Fighter)); + defTail->next = f; + defTail = f; + + root = cJSON_Parse(text); + + STRNCPY(f->name, cJSON_GetObjectItem(root, "name")->valuestring, MAX_NAME_LENGTH); + f->health = f->maxHealth = cJSON_GetObjectItem(root, "health")->valueint; + f->shield = f->maxShield = cJSON_GetObjectItem(root, "shield")->valueint; + f->speed = cJSON_GetObjectItem(root, "speed")->valueint; + f->reloadTime = cJSON_GetObjectItem(root, "reloadTime")->valueint; + f->shieldRechargeRate = cJSON_GetObjectItem(root, "shieldRechargeRate")->valueint; + f->texture = getTexture(cJSON_GetObjectItem(root, "textureName")->valuestring); + + if (cJSON_GetObjectItem(root, "guns")) + { + i = 0; + + for (node = cJSON_GetObjectItem(root, "guns")->child ; node != NULL ; node = node->next) + { + f->guns[i].type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + f->guns[i].x = cJSON_GetObjectItem(node, "x")->valueint; + f->guns[i].y = cJSON_GetObjectItem(node, "y")->valueint; + + i++; + + if (i >= MAX_FIGHTER_GUNS) + { + printf("ERROR: cannot assign more than %d guns to a fighter\n", MAX_FIGHTER_GUNS); + exit(1); + } + } + } + + if (cJSON_GetObjectItem(root, "missiles")) + { + node = cJSON_GetObjectItem(root, "missiles"); + + f->missiles.type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + f->missiles.ammo = f->missiles.maxAmmo = cJSON_GetObjectItem(node, "ammo")->valueint; + } + + SDL_QueryTexture(f->texture, NULL, NULL, &w, &h); + f->separationRadius = MAX(w, h); + f->separationRadius *= 2; + + cJSON_Delete(root); + free(text); +} + +void destroyFighterDefs(void) +{ + Fighter *f; + + while (defHead.next) + { + f = defHead.next; + defHead.next = f->next; + free(f); + } +} diff --git a/src/battle/fighterDefs.h b/src/battle/fighterDefs.h new file mode 100644 index 0000000..57d2506 --- /dev/null +++ b/src/battle/fighterDefs.h @@ -0,0 +1,30 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern char *readFile(char *filename); +extern SDL_Texture *getTexture(char *filename); +extern long lookup(char *name); + diff --git a/src/battle/fighters.c b/src/battle/fighters.c new file mode 100644 index 0000000..ac470f6 --- /dev/null +++ b/src/battle/fighters.c @@ -0,0 +1,478 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "fighters.h" + +static void separate(void); +static void die(void); +static void immediateDie(void); +static void spinDie(void); +static void straightDie(void); +static void randomizeDart(Fighter *dart); +static void randomizeDartGuns(Fighter *dart); + +Fighter *spawnFighter(char *name, int x, int y, int side) +{ + Fighter *f, *def; + + f = malloc(sizeof(Fighter)); + memset(f, 0, sizeof(Fighter)); + + def = getFighterDef(name); + + memcpy(f, def, sizeof(Fighter)); + + f->next = NULL; + + battle.fighterTail->next = f; + battle.fighterTail = f; + + f->x = x; + f->y = y; + f->side = side; + + switch (side) + { + case SIDE_ALLIES: + f->aggression = 1 + rand() % 3; + break; + + case SIDE_PIRATE: + f->aggression = rand() % 3; + break; + + case SIDE_PANDORAN: + f->aggression = 3 + rand() % 2; + break; + } + + if (strcmp(name, "ATAF") == 0) + { + f->aggression = 4; + } + + if (strcmp(name, "Dart") == 0) + { + randomizeDart(f); + } + + f->defaultAction = doAI; + f->die = die; + + return f; +} + +static void randomizeDart(Fighter *dart) +{ + char textureName[MAX_DESCRIPTION_LENGTH]; + + if (rand() % 5 == 0) + { + dart->health = dart->maxHealth = 5 + (rand() % 21); + } + + if (rand() % 5 == 0) + { + dart->shield = dart->maxShield = 1 + (rand() % 16); + dart->shieldRechargeRate = 30 + (rand() % 90); + } + + if (rand() % 5 == 0) + { + dart->speed = 2 + (rand() % 3); + } + + if (rand() % 5 == 0) + { + dart->reloadTime = 24 + (rand() % 11); + } + + randomizeDartGuns(dart); + + sprintf(textureName, "gfx/fighters/dart0%d.png", 1 + rand() % 7); + + dart->texture = getTexture(textureName); +} + +static void randomizeDartGuns(Fighter *dart) +{ + int i; + + switch (rand() % 4) + { + /* Single plasma gun */ + case 0: + dart->guns[0].type = BT_PLASMA; + dart->guns[0].x = dart->guns[0].y = 0; + + for (i = 1 ; i < MAX_FIGHTER_GUNS ; i++) + { + if (dart->guns[i].type) + { + dart->guns[i].type = BT_NONE; + } + } + break; + + /* Dual plasma guns */ + case 1: + dart->guns[0].type = BT_PLASMA; + dart->guns[1].type = BT_PLASMA; + + for (i = 2 ; i < MAX_FIGHTER_GUNS ; i++) + { + if (dart->guns[i].type) + { + dart->guns[i].type = BT_NONE; + } + } + break; + + /* Triple particle guns */ + case 2: + dart->guns[2].type = BT_PARTICLE; + dart->guns[2].y = -10; + break; + } +} + +void doFighters(void) +{ + Fighter *f, *prev; + + battle.numAllies = battle.numEnemies = 0; + + prev = &battle.fighterHead; + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + self = f; + + if (player != NULL) + { + if (f != player && f->health > 0) + { + separate(); + } + + if (f->side == player->side) + { + battle.numAllies++; + } + else + { + battle.numEnemies++; + } + } + + if (self->target != NULL && self->target->health <= 0) + { + self->action = self->defaultAction; + self->target = NULL; + } + + f->x += f->dx; + f->y += f->dy; + + if (f != player) + { + f->x -= battle.ssx; + f->y -= battle.ssy; + } + + if (f->health > 0) + { + f->reload = MAX(f->reload - 1, 0); + f->shieldRecharge = MAX(f->shieldRecharge - 1, 0); + f->armourHit = MAX(f->armourHit - 25, 0); + f->shieldHit = MAX(f->shieldHit - 5, 0); + + if (self->thrust > 0.25) + { + addEngineEffect(); + } + + if (!f->shieldRecharge) + { + f->shield = MIN(f->shield + 1, f->maxShield); + f->shieldRecharge = f->shieldRechargeRate; + } + + if (f->action == NULL && f->defaultAction != NULL) + { + f->action = f->defaultAction; + } + } + + if (f->action != NULL) + { + if (--f->thinkTime <= 0) + { + f->thinkTime = 0; + f->action(); + } + } + + if (f->health <= 0 && f->alive == ALIVE_ALIVE) + { + f->health = 0; + f->alive = ALIVE_DYING; + f->die(); + } + + if (f->alive == ALIVE_DEAD) + { + if (f == player) + { + battle.stats.playerKilled++; + } + else if (player != NULL) + { + if (player->alive == ALIVE_ALIVE) + { + if (f->side != player->side) + { + battle.stats.enemiesKilled++; + } + else + { + battle.stats.alliesKilled++; + + addHudMessage(colors.red, "Ally has been killed"); + } + } + + updateObjective(f->name); + } + + if (f == battle.fighterTail) + { + battle.fighterTail = prev; + } + + if (f == player) + { + player = NULL; + } + + prev->next = f->next; + free(f); + f = prev; + } + + prev = f; + } +} + +static void separate(void) +{ + int angle; + int distance; + float dx, dy, force; + int count; + Fighter *f; + + dx = dy = 0; + count = 0; + force = 0; + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + if (f != self) + { + distance = getDistance(f->x, f->y, self->x, self->y); + + if (distance > 0 && distance < self->separationRadius) + { + angle = getAngle(self->x, self->y, f->x, f->y); + + dx += sin(TO_RAIDANS(angle)); + dy += -cos(TO_RAIDANS(angle)); + force += (self->separationRadius - distance) * 0.005; + + count++; + } + } + } + + if (count > 0) + { + dx /= count; + dy /= count; + + dx *= force; + dy *= force; + + self->dx -= dx; + self->dy -= dy; + } +} + +void drawFighters(void) +{ + Fighter *f; + SDL_Rect r; + SDL_Texture *shieldHitTexture = getTexture("gfx/battle/shieldHit.png"); + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + SDL_SetTextureColorMod(f->texture, 255, 255, 255); + + if (f->armourHit > 0) + { + SDL_SetTextureColorMod(f->texture, 255, 255 - f->armourHit, 255 - f->armourHit); + } + + blitRotated(f->texture, f->x, f->y, f->angle); + + if (f->shieldHit > 0) + { + SDL_SetTextureBlendMode(shieldHitTexture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod(shieldHitTexture, f->shieldHit); + blit(shieldHitTexture, f->x, f->y, 1); + } + + if (player != NULL && f == player->target) + { + r.x = f->x - 32; + r.y = f->y - 32; + r.w = 64; + r.h = 64; + + SDL_SetRenderDrawColor(app.renderer, 255, 64, 0, 255); + SDL_RenderDrawRect(app.renderer, &r); + } + } +} + +void applyFighterThrust(void) +{ + float v; + + self->dx += sin(TO_RAIDANS(self->angle)) * 0.1; + self->dy += -cos(TO_RAIDANS(self->angle)) * 0.1; + self->thrust = sqrt((self->dx * self->dx) + (self->dy * self->dy)); + + if (self->thrust > self->speed * self->speed) + { + v = (self->speed / sqrt(self->thrust)); + self->dx = v * self->dx; + self->dy = v * self->dy; + } +} + +void applyFighterBrakes(void) +{ + self->dx *= 0.95; + self->dy *= 0.95; + + self->thrust = sqrt((self->dx * self->dx) + (self->dy * self->dy)); +} + +void damageFighter(Fighter *f, int damage) +{ + f->shield -= damage; + + if (f->shield < 0) + { + f->health -= abs(f->shield); + f->shield = 0; + f->armourHit = 255; + + playBattleSound(SND_ARMOUR_HIT, f->x, f->y); + } + else if (f->shield > 0) + { + f->shieldHit = 255; + } +} + +static void die(void) +{ + int n = rand() % 3; + if (self == player) + { + n = rand() % 2; + } + + switch (n) + { + case 0: + self->action = straightDie; + break; + + case 1: + self->action = spinDie; + break; + + case 2: + self->action = immediateDie; + break; + } +} + +static void immediateDie(void) +{ + self->alive = ALIVE_DEAD; + addFighterExplosion(); + playBattleSound(SND_EXPLOSION_1 + rand() % 4, self->x, self->y); +} + +static void spinDie(void) +{ + self->health--; + self->thinkTime = 0; + self->armourHit = 0; + self->shieldHit = 0; + + self->angle += 8; + + if (rand() % 2 == 0) + { + addSmallFighterExplosion(); + } + + if (self->health <= -FPS) + { + self->alive = ALIVE_DEAD; + addFighterExplosion(); + playBattleSound(SND_EXPLOSION_1 + rand() % 4, self->x, self->y); + } +} + +static void straightDie(void) +{ + self->health--; + self->thinkTime = 0; + self->armourHit = 0; + self->shieldHit = 0; + + if (rand() % 2 == 0) + { + addSmallFighterExplosion(); + } + + if (self->health <= -FPS) + { + self->alive = ALIVE_DEAD; + addFighterExplosion(); + playBattleSound(SND_EXPLOSION_1 + rand() % 4, self->x, self->y); + } +} diff --git a/src/battle/fighters.h b/src/battle/fighters.h new file mode 100644 index 0000000..594d838 --- /dev/null +++ b/src/battle/fighters.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern SDL_Texture *getTexture(char *filename); +extern void doAI(void); +extern void blitRotated(SDL_Texture *t, int x, int y, int angle); +extern void blit(SDL_Texture *t, int x, int y, int center); +extern float getAngle(int x1, int y1, int x2, int y2); +extern int getDistance(int x1, int y1, int x2, int y2); +extern void addEngineEffect(void); +extern void addFighterExplosion(void); +extern void addSmallFighterExplosion(void); +extern void playBattleSound(int id, int x, int y); +extern void updateObjective(char *name); +extern Fighter *getFighterDef(char *name); +extern void addHudMessage(SDL_Color c, char *format, ...); + +extern App app; +extern Battle battle; +extern Colors colors; +extern Fighter *player; +extern Fighter *self; diff --git a/src/battle/hud.c b/src/battle/hud.c new file mode 100644 index 0000000..2abfd25 --- /dev/null +++ b/src/battle/hud.c @@ -0,0 +1,312 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "hud.h" + +static void drawHealthShieldBar(int current, int max, int x, int y, int r, int g, int b); +static void drawPlayerTargeter(void); +static void drawNumAllies(void); +static void drawNumEnemies(void); +static void drawHealthBars(void); +static void drawMissileAmmoBar(void); +static void drawObjectives(void); +static void drawTargetDistance(void); +static void drawHudMessages(void); + +static HudMessage hudMessageHead; +static HudMessage *hudMessageTail; + +static int healthWarning; + +void initHud(void) +{ + healthWarning = 0; + + memset(&hudMessageHead, 0, sizeof(HudMessage)); + hudMessageTail = &hudMessageHead; +} + +void doHud(void) +{ + HudMessage *hudMessage, *prev; + + healthWarning++; + healthWarning %= FPS; + + prev = &hudMessageHead; + + for (hudMessage = hudMessageHead.next ; hudMessage != NULL ; hudMessage = hudMessage->next) + { + hudMessage->life--; + + if (hudMessage->life <= 0) + { + if (hudMessage == hudMessageTail) + { + hudMessageTail = prev; + } + + prev->next = hudMessage->next; + free(hudMessage); + hudMessage = prev; + } + + prev = hudMessage; + } +} + +void addHudMessage(SDL_Color c, char *format, ...) +{ + va_list args; + + HudMessage *hudMessage = malloc(sizeof(HudMessage)); + memset(hudMessage, 0, sizeof(HudMessage)); + hudMessageTail->next = hudMessage; + hudMessageTail = hudMessage; + + va_start(args, format); + vsprintf(hudMessageTail->message, format, args); + va_end(args); + + hudMessage->color = c; + hudMessage->life = FPS * 5; +} + +void drawHud(void) +{ + if (player != NULL) + { + drawHealthBars(); + + drawMissileAmmoBar(); + + drawNumAllies(); + + drawNumEnemies(); + + drawObjectives(); + + drawRadar(); + } + + drawHudMessages(); +} + +static void drawHealthBars(void) +{ + float p; + int r, g, b; + + drawPlayerTargeter(); + + r = g = b = 0; + p = player->health; + p /= player->maxHealth; + + if (p <= 0.25) + { + r = 255; + } + else if (p <= 0.5) + { + r = 255; + g = 255; + } + else + { + g = 200; + } + + drawHealthShieldBar(player->health, player->maxHealth, 10, 10, r, g, b); + drawHealthShieldBar(player->shield, player->maxShield, 10, 30, 0, 200, 255); + + if (player->target) + { + drawHealthShieldBar(player->target->health, player->target->maxHealth, SCREEN_WIDTH - 260, 10, 0, 200, 0); + drawHealthShieldBar(player->target->shield, player->target->maxShield, SCREEN_WIDTH - 260, 30, 0, 200, 255); + drawTargetDistance(); + } +} + +static void drawHealthShieldBar(int current, int max, int x, int y, int r, int g, int b) +{ + SDL_Rect rect; + float percent = 0; + + if (max > 0) + { + percent = current; + percent /= max; + } + + rect.x = x; + rect.y = y; + rect.w = 250; + rect.h = 12; + + SDL_SetRenderDrawColor(app.renderer, r / 2, g / 2, b / 2, 255); + SDL_RenderFillRect(app.renderer, &rect); + + SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255); + SDL_RenderDrawRect(app.renderer, &rect); + + if (current > 0) + { + rect.x += 2; + rect.y += 2; + rect.w -= 4; + rect.h -= 4; + + rect.w *= percent; + + SDL_SetRenderDrawColor(app.renderer, r, g, b, 255); + SDL_RenderFillRect(app.renderer, &rect); + } +} + +static void drawMissileAmmoBar(void) +{ + int w; + float i, percent, step; + SDL_Rect rect; + + rect.x = 10; + rect.y = 50; + rect.w = 250; + rect.h = 12; + + SDL_SetRenderDrawColor(app.renderer, 128, 64, 32, 255); + SDL_RenderFillRect(app.renderer, &rect); + + SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255); + SDL_RenderDrawRect(app.renderer, &rect); + + rect.x += 2; + rect.y += 2; + rect.w -= 4; + rect.h -= 4; + + percent = player->missiles.ammo; + percent /= player->missiles.maxAmmo; + + step = rect.w; + step /= player->missiles.maxAmmo; + + w = rect.w; + + rect.w *= percent; + + SDL_SetRenderDrawColor(app.renderer, 255, 128, 0, 255); + SDL_RenderFillRect(app.renderer, &rect); + + for (i = step ; i < w ; i += step) + { + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + SDL_RenderDrawLine(app.renderer, rect.x + i, rect.y, rect.x + i, rect.y + rect.h); + } +} + +static void drawPlayerTargeter(void) +{ + float angle; + int x, y; + + if (player->target) + { + angle = getAngle(player->x, player->y, player->target->x, player->target->y); + x = player->x; + y = player->y; + + x += sin(TO_RAIDANS(angle)) * 44; + y += -cos(TO_RAIDANS(angle)) * 44; + + blitRotated(getTexture("gfx/hud/targetPointer.png"), x, y, angle); + + blitRotated(getTexture("gfx/hud/targetCircle.png"), player->x, player->y, angle); + } +} + +static void drawNumAllies(void) +{ + SDL_Texture *t = getTexture("gfx/hud/smallFighter.png"); + + SDL_SetTextureColorMod(t, 150, 200, 255); + + blit(t, 400, 15, 0); + + drawText(435, 11, 14, TA_CENTER, colors.white, "(%d)", battle.numAllies); +} + +static void drawNumEnemies(void) +{ + SDL_Texture *t = getTexture("gfx/hud/smallFighter.png"); + + SDL_SetTextureColorMod(t, 255, 100, 100); + + blit(t, SCREEN_WIDTH - 410, 15, 0); + + drawText(SCREEN_WIDTH - 430, 11, 14, TA_CENTER, colors.white, "(%d)", battle.numEnemies); +} + +static void drawObjectives(void) +{ + drawText(SCREEN_WIDTH / 2, 10, 16, TA_CENTER, colors.white, "%d / %d", battle.numObjectivesComplete, battle.numObjectivesTotal); +} + +static void drawTargetDistance(void) +{ + float distance; + + if (player->target != NULL) + { + distance = getDistance(player->x, player->y, player->target->x, player->target->y); + distance /= 50; + + distance = (int)distance; + distance /= 10; + + drawText(SCREEN_WIDTH - 15, 50, 14, TA_RIGHT, colors.red, "Target: %.2fkm", distance); + } +} + +static void drawHudMessages(void) +{ + HudMessage *hudMessage; + int y = SCREEN_HEIGHT - 25; + + for (hudMessage = hudMessageHead.next ; hudMessage != NULL ; hudMessage = hudMessage->next) + { + drawText(10, y, 14, TA_LEFT, hudMessage->color, hudMessage->message); + + y -= 25; + } +} + +void resetHud(void) +{ + HudMessage *hudMessage; + + while (hudMessageHead.next) + { + hudMessage = hudMessageHead.next; + hudMessageHead.next = hudMessage->next; + free(hudMessage); + } +} diff --git a/src/battle/hud.h b/src/battle/hud.h new file mode 100644 index 0000000..993f2e8 --- /dev/null +++ b/src/battle/hud.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern SDL_Texture *getTexture(char *filename); +extern void blitRotated(SDL_Texture *texture, int x, int y, int angle); +extern void blit(SDL_Texture *texture, int x, int y, int center); +extern float getAngle(int x1, int y1, int x2, int y2); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern int getDistance(int x1, int y1, int x2, int y2); +extern void drawRadar(void); + +extern App app; +extern Battle battle; +extern Colors colors; +extern Fighter *player; diff --git a/src/battle/missionInfo.c b/src/battle/missionInfo.c new file mode 100644 index 0000000..664b3da --- /dev/null +++ b/src/battle/missionInfo.c @@ -0,0 +1,153 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "missionInfo.h" + +static void drawMissionSummary(SDL_Texture *title); + +static SDL_Texture *missionStartTexture; +static SDL_Texture *missionInProgressTexture; +static SDL_Texture *missionCompleteTexture; +static SDL_Texture *missionFailedTexture; +static const char *objectiveStatus[] = {"Incomplete", "Complete", "Failed", "Condition"}; + +void initMissionInfo(void) +{ + missionStartTexture = getTexture("gfx/battle/missionStart.png"); + missionInProgressTexture = getTexture("gfx/battle/missionInProgress.png"); + missionCompleteTexture = getTexture("gfx/battle/missionComplete.png"); + missionFailedTexture = getTexture("gfx/battle/missionFailed.png"); +} + +void drawMissionInfo(void) +{ + switch (battle.status) + { + case MS_START: + drawMissionSummary(missionStartTexture); + drawWidgets("startBattle"); + break; + + case MS_PAUSED: + drawMissionSummary(missionInProgressTexture); + drawWidgets("startBattle"); + break; + + case MS_COMPLETE: + case MS_FAILED: + if (battle.missionFinishedTimer <= -FPS) + { + drawMissionSummary(battle.status == MS_COMPLETE ? missionCompleteTexture : missionFailedTexture); + + if (battle.missionFinishedTimer <= -(FPS * 2)) + { + drawWidgets(battle.status == MS_COMPLETE ? "battleWon" : "battleLost"); + } + } + break; + } +} + +static void drawMissionSummary(SDL_Texture *header) +{ + Objective *o; + Challenge *c; + int y; + SDL_Color color; + char *challengeStatus; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); + SDL_RenderFillRect(app.renderer, NULL); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + blit(header, SCREEN_WIDTH / 2, 150, 1); + + y = 215; + + drawText(SCREEN_WIDTH / 2, 215, 28, TA_CENTER, colors.white, "OBJECTIVES"); + + y += 10; + + for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) + { + y += 50; + + switch (o->status) + { + case OS_INCOMPLETE: + color = colors.white; + break; + + case OS_COMPLETE: + color = colors.green; + break; + + case OS_FAILED: + color = colors.red; + break; + } + + drawText(SCREEN_WIDTH / 2 - 100, y, 22, TA_RIGHT, colors.white, o->description); + drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, "%d / %d", o->currentValue, o->targetValue); + drawText(SCREEN_WIDTH / 2 + 100, y, 22, TA_LEFT, color, objectiveStatus[o->status]); + } + + if (!battle.objectiveHead.next) + { + y += 50; + + drawText(SCREEN_WIDTH / 2, y, 22, TA_CENTER, colors.white, "(none)"); + } + + if (game.currentMission && game.currentMission->challengeHead.next) + { + y += 100; + + drawText(SCREEN_WIDTH / 2, y, 28, TA_CENTER, colors.white, "CHALLENGES"); + + y += 10; + + for (c = game.currentMission->challengeHead.next ; c != NULL ; c = c->next) + { + y += 50; + + color = colors.white; + + challengeStatus = "Incomplete"; + + if (c->passed) + { + color = colors.green; + + challengeStatus = "Complete"; + } + else if (battle.status == MS_COMPLETE ||battle.status == MS_FAILED) + { + color = colors.red; + + challengeStatus = "Failed"; + } + + drawText(SCREEN_WIDTH / 2 - 25, y, 22, TA_RIGHT, colors.white, "%s", getChallengeDescription(c)); + drawText(SCREEN_WIDTH / 2 + 25, y, 22, TA_LEFT, color, challengeStatus); + } + } +} diff --git a/src/battle/missionInfo.h b/src/battle/missionInfo.h new file mode 100644 index 0000000..042ec34 --- /dev/null +++ b/src/battle/missionInfo.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void blit(SDL_Texture *texture, int x, int y, int center); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern SDL_Texture *getTexture(char *filename); +extern char *getChallengeDescription(Challenge *c); +extern void drawWidgets(char *groupName); + +extern App app; +extern Battle battle; +extern Colors colors; +extern Game game; diff --git a/src/battle/objectives.c b/src/battle/objectives.c new file mode 100644 index 0000000..b995505 --- /dev/null +++ b/src/battle/objectives.c @@ -0,0 +1,103 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "objectives.h" + +void failIncompleteObjectives(void); + +void doObjectives(void) +{ + Objective *o; + + battle.numObjectivesComplete = battle.numObjectivesTotal = 0; + + for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) + { + battle.numObjectivesTotal++; + + if (o->currentValue == o->targetValue) + { + battle.numObjectivesComplete++; + } + } + + if (battle.numObjectivesTotal > 0 && battle.numObjectivesComplete == battle.numObjectivesTotal && battle.status == MS_IN_PROGRESS) + { + battle.status = MS_COMPLETE; + battle.missionFinishedTimer = FPS; + + game.stats.missionsCompleted++; + + updateChallenges(); + } +} + +void updateObjective(char *name) +{ + Objective *o; + + for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) + { + if (strcmp(o->targetName, name) == 0) + { + o->currentValue = MIN(o->targetValue, o->currentValue + 1); + + if (o->targetValue - o->currentValue <= 10) + { + addHudMessage(colors.cyan, "%s - %d / %d", o->description, o->currentValue, o->targetValue); + } + else if (o->currentValue % 10 == 0) + { + addHudMessage(colors.cyan, "%s - %d / %d", o->description, o->currentValue, o->targetValue); + } + + if (o->currentValue == o->targetValue) + { + switch (o->status) + { + case OS_INCOMPLETE: + o->status = OS_COMPLETE; + addHudMessage(colors.green, "%s - Objective Complete!", o->description); + break; + + case OS_CONDITIONAL: + o->status = OS_FAILED; + battle.status = MS_FAILED; + battle.missionFinishedTimer = FPS; + failIncompleteObjectives(); + break; + } + } + } + } +} + +void failIncompleteObjectives(void) +{ + Objective *o; + + for (o = battle.objectiveHead.next ; o != NULL ; o = o->next) + { + if (o->status != OS_COMPLETE) + { + o->status = OS_FAILED; + } + } +} diff --git a/src/battle/objectives.h b/src/battle/objectives.h new file mode 100644 index 0000000..bc8252f --- /dev/null +++ b/src/battle/objectives.h @@ -0,0 +1,31 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void updateChallenges(void); +extern void addHudMessage(SDL_Color c, char *format, ...); + +extern Battle battle; +extern Colors colors; +extern Game game; diff --git a/src/battle/player.c b/src/battle/player.c new file mode 100644 index 0000000..95dc1a4 --- /dev/null +++ b/src/battle/player.c @@ -0,0 +1,106 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "player.h" + +static void selectTarget(void); + +void doPlayer(void) +{ + if (player != NULL) + { + self = player; + + if (player->alive == ALIVE_ALIVE) + { + if (app.keyboard[SDL_SCANCODE_LEFT]) + { + player->angle -= 4; + } + + if (app.keyboard[SDL_SCANCODE_RIGHT]) + { + player->angle += 4; + } + + if (app.keyboard[SDL_SCANCODE_UP]) + { + applyFighterThrust(); + } + + if (app.keyboard[SDL_SCANCODE_DOWN]) + { + applyFighterBrakes(); + } + + if (app.keyboard[SDL_SCANCODE_LCTRL] && !player->reload && player->guns[0].type) + { + fireGuns(player); + } + + if (app.keyboard[SDL_SCANCODE_RETURN] && player->missiles.ammo && player->target) + { + fireMissile(player); + + app.keyboard[SDL_SCANCODE_RETURN] = 0; + } + + if (!player->target || player->target->health <= 0 || getDistance(player->x, player->y, player->target->x, player->target->y) > 1000) + { + selectTarget(); + } + } + + player->angle = player->angle % 360; + + player->x = SCREEN_WIDTH / 2; + player->y = SCREEN_HEIGHT / 2; + + if (player->health <= 0 && battle.status == MS_IN_PROGRESS) + { + failIncompleteObjectives(); + + battle.status = MS_FAILED; + battle.missionFinishedTimer = FPS; + } + } +} + +static void selectTarget(void) +{ + unsigned int closest = 65535; + unsigned int dist = 65535; + Fighter *f; + + player->target = NULL; + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + if (f != player && f->side != player->side && f->alive == ALIVE_ALIVE) + { + dist = getDistance(self->x, self->y, f->x, f->y); + if (dist < closest) + { + player->target = f; + closest = dist; + } + } + } +} diff --git a/src/battle/player.h b/src/battle/player.h new file mode 100644 index 0000000..5e4ee36 --- /dev/null +++ b/src/battle/player.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void fireGuns(Fighter *owner); +extern void fireMissile(Fighter *owner); +extern void applyFighterThrust(void); +extern void applyFighterBrakes(void); +extern int getDistance(int x1, int y1, int x2, int y2); +extern void failIncompleteObjectives(void); + +extern App app; +extern Battle battle; +extern Fighter *player; +extern Fighter *self; diff --git a/src/battle/radar.c b/src/battle/radar.c new file mode 100644 index 0000000..2494147 --- /dev/null +++ b/src/battle/radar.c @@ -0,0 +1,74 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "radar.h" + +void drawRadar(void) +{ + SDL_Rect r; + Fighter *f; + + drawFilledCircle(SCREEN_WIDTH - 85, SCREEN_HEIGHT - 85, 75, 0, 128, 0, 32); + + drawCircle(SCREEN_WIDTH - 85, SCREEN_HEIGHT - 85, 25, 0, 255, 0, 64); + drawCircle(SCREEN_WIDTH - 85, SCREEN_HEIGHT - 85, 50, 0, 255, 0, 64); + + r.w = r.h = 3; + + for (f = battle.fighterHead.next ; f != NULL ; f = f->next) + { + if (getDistance(f->x, f->y, player->x, player->y) / 15 < 70) + { + r.x = SCREEN_WIDTH - 85; + r.y = SCREEN_HEIGHT - 85; + + r.x -= (player->x - f->x) / 15; + r.y -= (player->y - f->y) / 15; + + r.x--; + r.y--; + + switch (f->side) + { + case SIDE_ALLIES: + SDL_SetRenderDrawColor(app.renderer, 0, 255, 0, 255); + break; + + case SIDE_PIRATE: + case SIDE_PANDORAN: + SDL_SetRenderDrawColor(app.renderer, 255, 0, 0, 255); + break; + + case SIDE_NONE: + SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255); + break; + } + + if (player->target == f) + { + SDL_SetRenderDrawColor(app.renderer, 255, 255, 0, 255); + } + + SDL_RenderFillRect(app.renderer, &r); + } + } + + drawCircle(SCREEN_WIDTH - 85, SCREEN_HEIGHT - 85, 75, 0, 255, 0, 128); +} diff --git a/src/battle/radar.h b/src/battle/radar.h new file mode 100644 index 0000000..237161d --- /dev/null +++ b/src/battle/radar.h @@ -0,0 +1,32 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void drawCircle(int cx, int cy, int radius, int r, int g, int b, int a); +extern void drawFilledCircle(int cx, int cy, int radius, int r, int g, int b, int a); +extern int getDistance(int x1, int y1, int x2, int y2); + +extern App app; +extern Battle battle; +extern Fighter *player; diff --git a/src/battle/starfield.c b/src/battle/starfield.c new file mode 100644 index 0000000..fe569e9 --- /dev/null +++ b/src/battle/starfield.c @@ -0,0 +1,85 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "starfield.h" + +Star stars[MAX_STARS]; + +void initStars(void) +{ + int i; + + memset(stars, 0, sizeof(Star) * MAX_STARS); + + for (i = 0 ; i < MAX_STARS ; i++) + { + stars[i].x = rand() % SCREEN_WIDTH; + stars[i].y = rand() % SCREEN_HEIGHT; + stars[i].speed = 10 + rand() % 30; + + stars[i].speed *= 0.1; + } +} + +void doStars(float dx, float dy) +{ + int i; + + for (i = 0 ; i < MAX_STARS ; i++) + { + stars[i].x -= (dx * stars[i].speed); + stars[i].y -= (dy * stars[i].speed); + + if (stars[i].x >= SCREEN_WIDTH) + { + stars[i].x = 0; + } + + if (stars[i].x < 0) + { + stars[i].x = SCREEN_WIDTH - 1; + } + + if (stars[i].y >= SCREEN_HEIGHT) + { + stars[i].y = 0; + } + + if (stars[i].y < 0) + { + stars[i].y = SCREEN_HEIGHT - 1; + } + } +} + +void drawStars(void) +{ + int i; + int c; + + for (i = 0 ; i < MAX_STARS ; i++) + { + c = 85 * stars[i].speed; + + SDL_SetRenderDrawColor(app.renderer, c, c, c, 255); + + SDL_RenderDrawPoint(app.renderer, stars[i].x, stars[i].y); + } +} diff --git a/src/battle/starfield.h b/src/battle/starfield.h new file mode 100644 index 0000000..ce53166 --- /dev/null +++ b/src/battle/starfield.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern App app; diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..456842a --- /dev/null +++ b/src/defs.h @@ -0,0 +1,154 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#define PI 3.14159265358979323846 +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define STRNCPY(dest, src, n) strncpy(dest, src, n); dest[n - 1] = '\0' +#define TO_RAIDANS(angleDegrees) (angleDegrees * PI / 180.0) +#define TO_DEGREES(angleRadians) (angleRadians * 180.0 / PI) + +#define SCREEN_WIDTH 1280 +#define SCREEN_HEIGHT 720 + +#define MAX_KEYBOARD_KEYS 350 + +#define LOGIC_RATE 16 +#define FPS (1000 / LOGIC_RATE) + +#define MAX_NAME_LENGTH 32 +#define MAX_DESCRIPTION_LENGTH 512 +#define MAX_FILENAME_LENGTH 1024 + +#define NUM_TEXTURE_BUCKETS 32 + +#define MAX_STARS 500 + +#define MAX_FONTS 32 +#define NUM_TEXT_BUCKETS 64 + +#define MAX_FIGHTER_GUNS 12 + +#define BF_NONE 0 +#define BF_ENGINE (2 << 0) + +enum +{ + TA_LEFT, + TA_RIGHT, + TA_CENTER +}; + +enum +{ + ALIVE_ALIVE, + ALIVE_DYING, + ALIVE_DEAD +}; + +enum +{ + SIDE_NONE, + SIDE_ALLIES, + SIDE_PANDORAN, + SIDE_PIRATE, + SIDE_CSN, + SIDE_INF, + SIDE_UNF +}; + +enum +{ + BT_NONE, + BT_PARTICLE, + BT_PLASMA, + BT_LASER, + BT_MAG, + BT_ROCKET, + BT_MISSILE, + BT_FF, + BT_MAX +}; + +enum +{ + MISSILE_ROCKET, + MISSILE_MISSILE, + MISSILE_FF, + MISSILE_MAX +}; + +enum +{ + EFFECT_LINE, + EFFECT_TEXTURE, + EFFECT_HALO +}; + +enum +{ + SND_PARTICLE, + SND_PLASMA, + SND_MAG, + SND_ARMOUR_HIT, + SND_SHIELD_HIT, + SND_EXPLOSION_1, + SND_EXPLOSION_2, + SND_EXPLOSION_3, + SND_EXPLOSION_4, + SND_GUI_CLICK, + SND_GUI_SELECT, + SND_GUI_CLOSE, + SND_GUI_DENIED, + SND_MAX +}; + +enum +{ + OS_INCOMPLETE, + OS_COMPLETE, + OS_FAILED, + OS_CONDITIONAL +}; + +enum +{ + MS_START, + MS_IN_PROGRESS, + MS_PAUSED, + MS_COMPLETE, + MS_FAILED +}; + +enum { + WT_BUTTON, + WT_SELECT +}; + +enum +{ + CHALLENGE_ARMOUR, + CHALLENGE_TIME, + CHALLENGE_ACCURACY, + CHALLENGE_NO_LOSSES, + CHALLENGE_1_LOSS, + CHALLENGE_LOSSES, + CHALLENGE_PLAYER_KILLS +}; diff --git a/src/draw/draw.c b/src/draw/draw.c new file mode 100644 index 0000000..3aa54c6 --- /dev/null +++ b/src/draw/draw.c @@ -0,0 +1,208 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "draw.h" + +static PointF backgroundPoint[4]; + +void initBackground(void) +{ + backgroundPoint[0].x = -SCREEN_WIDTH / 2; + backgroundPoint[0].y = -SCREEN_HEIGHT / 2; + + backgroundPoint[1].x = SCREEN_WIDTH / 2; + backgroundPoint[1].y = -SCREEN_HEIGHT / 2; + + backgroundPoint[2].x = -SCREEN_WIDTH / 2; + backgroundPoint[2].y = SCREEN_HEIGHT / 2; + + backgroundPoint[3].x = SCREEN_WIDTH / 2; + backgroundPoint[3].y = SCREEN_HEIGHT / 2; +} + +void prepareScene(void) +{ + SDL_SetRenderTarget(app.renderer, app.backBuffer); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(app.renderer); +} + +void presentScene(void) +{ + /*drawText(SCREEN_WIDTH - 5, 35, 14, TA_RIGHT, colors.white, "FPS: %d", app.fps);*/ + + SDL_SetRenderTarget(app.renderer, NULL); + SDL_RenderCopy(app.renderer, app.backBuffer, NULL, NULL); + SDL_RenderPresent(app.renderer); +} + +void blit(SDL_Texture *texture, int x, int y, int center) +{ + SDL_Rect dstRect; + + dstRect.x = x; + dstRect.y = y; + SDL_QueryTexture(texture, NULL, NULL, &dstRect.w, &dstRect.h); + + if (center) + { + dstRect.x -= (dstRect.w / 2); + dstRect.y -= (dstRect.h / 2); + } + + SDL_RenderCopy(app.renderer, texture, NULL, &dstRect); +} + +void blitScaled(SDL_Texture *texture, int x, int y, int w, int h) +{ + SDL_Rect dstRect; + + dstRect.x = x; + dstRect.y = y; + dstRect.w = w; + dstRect.h = h; + + SDL_RenderCopy(app.renderer, texture, NULL, &dstRect); +} + +void blitRotated(SDL_Texture *texture, int x, int y, int angle) +{ + SDL_Rect dstRect; + + dstRect.x = x; + dstRect.y = y; + SDL_QueryTexture(texture, NULL, NULL, &dstRect.w, &dstRect.h); + dstRect.x -= (dstRect.w / 2); + dstRect.y -= (dstRect.h / 2); + + SDL_RenderCopyEx(app.renderer, texture, NULL, &dstRect, angle, NULL, SDL_FLIP_NONE); +} + +void drawCircle(int cx, int cy, int radius, int r, int g, int b, int a) +{ + int x = radius; + int y = 0; + int radiusError = 1 - x; + + SDL_SetRenderDrawColor(app.renderer, r, g, b, a); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + + while (x >= y) + { + SDL_RenderDrawPoint(app.renderer, x + cx, y + cy); + SDL_RenderDrawPoint(app.renderer, y + cx, x + cy); + SDL_RenderDrawPoint(app.renderer,-x + cx, y + cy); + SDL_RenderDrawPoint(app.renderer,-y + cx, x + cy); + SDL_RenderDrawPoint(app.renderer,-x + cx, -y + cy); + SDL_RenderDrawPoint(app.renderer,-y + cx, -x + cy); + SDL_RenderDrawPoint(app.renderer, x + cx, -y + cy); + SDL_RenderDrawPoint(app.renderer, y + cx, -x + cy); + + y++; + + if (radiusError < 0) + { + radiusError += 2 * y + 1; + } + else + { + x--; + radiusError += 2 * (y - x) + 1; + } + } + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); +} + +void drawFilledCircle(int cx, int cy, int radius, int r, int g, int b, int a) +{ + int x, y; + + SDL_SetRenderDrawColor(app.renderer, r, g, b, a); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + + for (y = -radius ; y <= radius ; y++) + { + for (x =- radius; x <= radius ; x++) + { + if (x * x + y * y <= radius * radius) + { + SDL_RenderDrawPoint(app.renderer, cx + x, cy + y); + } + } + } + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); +} + +void scrollBackground(float x, float y) +{ + int i; + + for (i = 0 ; i < 4 ; i++) + { + backgroundPoint[i].x += x; + backgroundPoint[i].y += y; + + if (backgroundPoint[i].x < 0) + { + backgroundPoint[i].x += (SCREEN_WIDTH * 2); + } + + if (backgroundPoint[i].x >= SCREEN_WIDTH) + { + backgroundPoint[i].x -= (SCREEN_WIDTH * 2); + } + + if (backgroundPoint[i].y < 0) + { + backgroundPoint[i].y += (SCREEN_HEIGHT * 2); + } + + if (backgroundPoint[i].y >= SCREEN_HEIGHT) + { + backgroundPoint[i].y -= (SCREEN_HEIGHT * 2); + } + } +} + +void drawBackground(SDL_Texture *texture) +{ + int i; + + for (i = 0 ; i < 4 ; i++) + { + blitScaled(texture, backgroundPoint[i].x, backgroundPoint[i].y, SCREEN_WIDTH, SCREEN_HEIGHT); + } +} + +void saveScreenshot(void) +{ + static int i = 0; + char filename[MAX_NAME_LENGTH]; + SDL_Surface *sshot; + + sprintf(filename, "/home/steve/temp/screenshots/%d.bmp", ++i); + + sshot = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + SDL_RenderReadPixels(app.renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); + SDL_SaveBMP(sshot, filename); + SDL_FreeSurface(sshot); +} diff --git a/src/draw/draw.h b/src/draw/draw.h new file mode 100644 index 0000000..8791ac1 --- /dev/null +++ b/src/draw/draw.h @@ -0,0 +1,29 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); + +extern App app; +extern Colors colors; diff --git a/src/draw/text.c b/src/draw/text.c new file mode 100644 index 0000000..7ca8e72 --- /dev/null +++ b/src/draw/text.c @@ -0,0 +1,266 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "text.h" + +static void loadFont(int size); +static SDL_Texture *getCachedText(unsigned long hash); +static void cacheText(unsigned long hash, SDL_Texture *t); +static unsigned long hashcode(const char *str, int size); +static void drawTextNormal(int x, int y, int size, int align, SDL_Color c, char *text); +static void drawTextSplit(int x, int y, int size, int align, SDL_Color c, char *text); +void textSize(char *text, int size, int *w, int *h); + +static char drawTextBuffer[MAX_DESCRIPTION_LENGTH]; +static TTF_Font *font[MAX_FONTS]; +static Texture textures[NUM_TEXT_BUCKETS]; +static int maxWidth = 0; + +void initFonts(void) +{ + memset(&font, 0, sizeof(TTF_Font*) * MAX_FONTS); + memset(&textures, 0, sizeof(Texture) * NUM_TEXT_BUCKETS); +} + +void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...) +{ + va_list args; + + memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer)); + + va_start(args, format); + vsprintf(drawTextBuffer, format, args); + va_end(args); + + if (maxWidth == 0) + { + drawTextNormal(x, y, size, align, c, drawTextBuffer); + } + else + { + drawTextSplit(x, y, size, align, c, drawTextBuffer); + } +} + +static void drawTextNormal(int x, int y, int size, int align, SDL_Color c, char *text) +{ + SDL_Surface *surface; + SDL_Texture *t; + int w, h; + long hash; + + if (size >= MAX_FONTS) + { + printf("ERROR: %d exceeds max font size index of %d\n", size, MAX_FONTS); + exit(1); + } + + if (!font[size]) + { + loadFont(size); + } + + hash = hashcode(text, size); + + t = getCachedText(hash); + + if (!t) + { + surface = TTF_RenderText_Blended(font[size], text, c); + t = SDL_CreateTextureFromSurface(app.renderer, surface); + SDL_FreeSurface(surface); + + cacheText(hash, t); + } + + SDL_QueryTexture(t, NULL, NULL, &w, &h); + + if (align == TA_CENTER) + { + x -= (w / 2); + } + else if (align == TA_RIGHT) + { + x -= w; + } + + SDL_SetTextureColorMod(t, c.r, c.g, c.b); + + blit(t, x, y, 0); +} + +static void drawTextSplit(int x, int y, int size, int align, SDL_Color c, char *text) +{ + char drawTextBuffer[MAX_DESCRIPTION_LENGTH]; + char *token; + int w, h, currentWidth; + + memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer)); + + token = strtok(text, " "); + + currentWidth = 0; + + while (token) + { + textSize(token, size, &w, &h); + + if (currentWidth + w > maxWidth) + { + drawTextNormal(x, y, size, align, c, drawTextBuffer); + + currentWidth = 0; + y += h; + memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer)); + } + + strcat(drawTextBuffer, token); + strcat(drawTextBuffer, " "); + + currentWidth += w; + + token = strtok(NULL, " "); + } + + drawTextNormal(x, y, size, align, c, drawTextBuffer); +} + +void textSize(char *text, int size, int *w, int *h) +{ + if (!font[size]) + { + loadFont(size); + } + + TTF_SizeText(font[size], text, w, h); +} + +void limitTextWidth(int width) +{ + maxWidth = width; +} + +static SDL_Texture *getCachedText(unsigned long hash) +{ + Texture *t; + int i; + + i = hash % NUM_TEXT_BUCKETS; + + t = textures[i].next; + + for (t = textures[i].next ; t != NULL ; t = t->next) + { + if (t->hash == hash) + { + return t->texture; + } + } + + return NULL; +} + +static void cacheText(unsigned long hash, SDL_Texture *texture) +{ + Texture *t, *new; + int i; + + i = hash % NUM_TEXT_BUCKETS; + + t = &textures[i]; + + /* horrible bit to find the tail */ + while (t->next) + { + t = t->next; + } + + new = malloc(sizeof(Texture)); + memset(new, 0, sizeof(Texture)); + + new->hash = hash; + new->texture = texture; + + t->next = new; +} + +void expireTexts(void) +{ + Texture *t; + int i, n; + + n = 0; + + for (i = 0 ; i < NUM_TEXT_BUCKETS ; i++) + { + while (textures[i].next) + { + t = textures[i].next; + textures[i].next = t->next; + SDL_DestroyTexture(t->texture); + free(t); + + n++; + } + + textures[i].next = NULL; + } + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Expired %d texts", n); +} + +static void loadFont(int size) +{ + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "loadFonts(%d)", size); + + font[size] = TTF_OpenFont("data/fonts/Roboto-Medium.ttf", size); +} + +static unsigned long hashcode(const char *str, int size) +{ + unsigned long hash = 5381; + int c; + + c = *str; + + while (c) + { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + c = *str++; + } + + hash = ((hash << 5) + hash) + size; + + return abs(hash); +} + +void destroyFonts(void) +{ + int i; + + for (i = 0 ; i < MAX_FONTS ; i++) + { + if (font[i]) + { + TTF_CloseFont(font[i]); + } + } +} diff --git a/src/draw/text.h b/src/draw/text.h new file mode 100644 index 0000000..7c5ef08 --- /dev/null +++ b/src/draw/text.h @@ -0,0 +1,29 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +#include "../defs.h" +#include "../structs.h" + +extern void blit(SDL_Texture *texture, int x, int y, int centered); + +extern App app; diff --git a/src/galaxy/galacticMap.c b/src/galaxy/galacticMap.c new file mode 100644 index 0000000..431d965 --- /dev/null +++ b/src/galaxy/galacticMap.c @@ -0,0 +1,726 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "galacticMap.h" + +static void logic(void); +static void draw(void); +static void handleKeyboard(void); +static void drawStarSystemDetail(void); +static void prevMission(void); +static void nextMission(void); +static void selectStarSystem(void); +static void drawGalaxy(void); +static void handleGalaxyKB(void); +static void handleSelectedSystemKB(void); +static void centerOnSelectedStarSystem(void); +static void doStarSystems(void); +void destroyGalacticMap(void); +static void drawPulses(void); +static void doPulses(void); +static void addPulses(void); +static void drawMenu(void); +static void resume(void); +static void stats(void); +static void options(void); +static void statsOK(void); +static void quit(void); +static void returnFromOptions(void); + +static StarSystem *selectedStarSystem; +static Mission *selectedMission = {0}; +static int missionListStart, selectedMissionIndex; +static SDL_Texture *background; +static SDL_Texture *starSystemTexture; +static SDL_Point camera; +static int viewingSystem; +static Pulse pulseHead = {0}; +static Pulse *pulseTail; +static int pulseTimer; +static float ssx, ssy; +static int show; +static int completedMissions, totalMissions; +static int completedChallenges, totalChallenges; + +void initGalacticMap(void) +{ + startSectionTransition(); + + stopMusic(); + + app.delegate.logic = &logic; + app.delegate.draw = &draw; + memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + background = getTexture("gfx/backgrounds/background02.jpg"); + + starSystemTexture = getTexture("gfx/galaxy/starSystem.png"); + + if (!selectedStarSystem) + { + selectedStarSystem = game.starSystemHead.next; + } + + updateStarSystemDescriptions(); + + centerOnSelectedStarSystem(); + + saveGame(); + + viewingSystem = 0; + + pulseTimer = 0; + + show = SHOW_GALAXY; + + /* clear the pulses */ + destroyGalacticMap(); + + initBackground(); + + getWidget("resume", "galacticMap")->action = resume; + getWidget("stats", "galacticMap")->action = stats; + getWidget("options", "galacticMap")->action = options; + getWidget("quit", "galacticMap")->action = quit; + + getWidget("ok", "stats")->action = statsOK; + + endSectionTransition(); + + playMusic("music/Pressure.ogg"); +} + +static void logic(void) +{ + handleKeyboard(); + + doStarSystems(); + + scrollBackground(-ssx, -ssy); + + doPulses(); + + doStars(ssx, ssy); + + if (pulseTimer % FPS == 0) + { + addPulses(); + } + + pulseTimer++; + pulseTimer %= (FPS * 60); + + doWidgets(); +} + +static void doStarSystems(void) +{ + StarSystem *starSystem; + int cx, cy; + + completedMissions = totalMissions = completedChallenges = totalChallenges = 0; + + cx = (camera.x + SCREEN_WIDTH / 2) - 32; + cy = (camera.y + SCREEN_HEIGHT / 2) - 32; + + selectedStarSystem = NULL; + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + completedMissions += starSystem->completedMissions; + totalMissions += starSystem->totalMissions; + completedChallenges += starSystem->completedChallenges; + totalChallenges += starSystem->totalChallenges; + + if (starSystem->totalMissions > 0 && collision(cx, cy, 64, 64, starSystem->x, starSystem->y, 4, 4)) + { + if (selectedStarSystem != starSystem) + { + selectedStarSystem = starSystem; + } + } + } +} + +static void addPulses(void) +{ + Pulse *pulse; + StarSystem *starSystem; + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + if (starSystem->completedMissions < starSystem->totalMissions) + { + pulse = malloc(sizeof(Pulse)); + memset(pulse, 0, sizeof(Pulse)); + + pulse->x = starSystem->x; + pulse->y = starSystem->y; + pulse->life = 255; + pulse->r = 255; + + pulseTail->next = pulse; + pulseTail = pulse; + } + else if (starSystem->completedChallenges < starSystem->totalChallenges && pulseTimer % (FPS * 2) == 0) + { + pulse = malloc(sizeof(Pulse)); + memset(pulse, 0, sizeof(Pulse)); + + pulse->x = starSystem->x; + pulse->y = starSystem->y; + pulse->life = 255; + pulse->r = pulse->g = 255; + + pulseTail->next = pulse; + pulseTail = pulse; + } + } +} + +static void doPulses(void) +{ + Pulse *pulse, *prev; + + prev = &pulseHead; + + for (pulse = pulseHead.next ; pulse != NULL ; pulse = pulse->next) + { + pulse->size += 0.5; + pulse->life--; + + if (pulse->life <= 0) + { + if (pulse == pulseTail) + { + pulseTail = prev; + } + + prev->next = pulse->next; + free(pulse); + pulse = prev; + } + + prev = pulse; + } +} + +static void draw(void) +{ + prepareScene(); + + drawBackground(background); + + drawStars(); + + drawGalaxy(); + + drawPulses(); + + if (viewingSystem) + { + drawStarSystemDetail(); + } + + switch (show) + { + case SHOW_GALAXY: + break; + + case SHOW_MENU: + drawMenu(); + break; + + case SHOW_STATS: + drawStats(); + break; + + case SHOW_OPTIONS: + drawOptions(); + break; + } + + presentScene(); +} + +static void drawPulses(void) +{ + Pulse *pulse; + + for (pulse = pulseHead.next ; pulse != NULL ; pulse = pulse->next) + { + drawCircle(pulse->x - camera.x, pulse->y - camera.y, pulse->size, pulse->r, pulse->g, pulse->b, pulse->life); + } +} + +static void centerOnSelectedStarSystem(void) +{ + camera.x = selectedStarSystem->x; + camera.x -= SCREEN_WIDTH / 2; + + camera.y = selectedStarSystem->y; + camera.y -= SCREEN_HEIGHT / 2; +} + +static void drawGalaxy(void) +{ + SDL_Rect r; + StarSystem *starSystem; + SDL_Color color; + + r.w = r.h = 64; + r.x = (SCREEN_WIDTH / 2) - r.w / 2; + r.y = (SCREEN_HEIGHT / 2) - r.h / 2; + + if (selectedStarSystem) + { + SDL_SetRenderDrawColor(app.renderer, 64, 100, 128, 255); + SDL_RenderFillRect(app.renderer, &r); + } + + SDL_SetRenderDrawColor(app.renderer, 128, 200, 255, 255); + SDL_RenderDrawRect(app.renderer, &r); + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + blit(starSystemTexture, starSystem->x - camera.x, starSystem->y - camera.y, 1); + + switch (starSystem->side) + { + case SIDE_CSN: + color = colors.cyan; + break; + + case SIDE_UNF: + color = colors.white; + break; + + case SIDE_INF: + color = colors.red; + break; + } + + drawText(starSystem->x - camera.x, starSystem->y - camera.y + 12, 14, TA_CENTER, color, starSystem->name); + } + + if (!viewingSystem && selectedStarSystem != NULL) + { + r.x = 0; + r.y = SCREEN_HEIGHT - 35; + r.w = SCREEN_WIDTH; + r.h = 35; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 200); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 30, 18, TA_CENTER, colors.white, selectedStarSystem->description); + } + + r.x = 0; + r.y = 0; + r.w = SCREEN_WIDTH; + r.h = 35; + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 200); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + drawText((SCREEN_WIDTH / 2) - 50, 5, 18, TA_RIGHT, colors.white, "Missions %d / %d", completedMissions, totalMissions); + drawText((SCREEN_WIDTH / 2) + 50, 5, 18, TA_LEFT, colors.white, "Challenges %d / %d", completedChallenges, totalChallenges); +} + +static void selectStarSystem(void) +{ + Mission *mission, *prev; + + selectedMission = selectedStarSystem->missionHead.next; + + selectedStarSystem->completedMissions = selectedStarSystem->totalMissions = 0; + + prev = &selectedStarSystem->missionHead; + + for (mission = selectedStarSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + selectedStarSystem->totalMissions++; + + if (mission->completed) + { + selectedStarSystem->completedMissions++; + } + + mission->available = prev->completed; + + prev = mission; + } + + missionListStart = 0; + selectedMissionIndex = 0; + + viewingSystem = selectedStarSystem->totalMissions > 0; +} + +static void drawStarSystemDetail(void) +{ + int y, i; + Mission *mission; + Challenge *challenge; + SDL_Rect r; + + r.w = 900; + r.h = 600; + r.x = (SCREEN_WIDTH / 2) - (r.w / 2); + r.y = (SCREEN_HEIGHT / 2) - (r.h / 2); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 225); + SDL_RenderFillRect(app.renderer, &r); + + SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 200); + SDL_RenderDrawRect(app.renderer, &r); + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + y = 70; + + drawText(SCREEN_WIDTH / 2, y, 28, TA_CENTER, colors.cyan, "%s (%d / %d)", selectedStarSystem->name, selectedStarSystem->completedMissions, selectedStarSystem->totalMissions); + + SDL_RenderDrawLine(app.renderer, r.x, 120, r.x + r.w, 120); + + SDL_RenderDrawLine(app.renderer, 515, 120, 515, 660); + + y += 80; + + r.x = 200; + r.w = 300; + r.h = 40; + + i = 0; + + for (mission = selectedStarSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + if (i == selectedMissionIndex) + { + selectedMission = mission; + } + + if (i >= missionListStart && i < missionListStart + 10) + { + if (mission == selectedMission) + { + r.y = y - 2; + + SDL_SetRenderDrawColor(app.renderer, 32, 64, 128, 255); + SDL_RenderFillRect(app.renderer, &r); + + SDL_SetRenderDrawColor(app.renderer, 64, 96, 196, 255); + SDL_RenderDrawRect(app.renderer, &r); + } + + if (mission->available) + { + drawText(210, y, 24, TA_LEFT, mission->completed ? colors.white : colors.yellow, mission->name); + } + else + { + drawText(210, y, 24, TA_LEFT, colors.darkGrey, "[LOCKED]"); + } + + y += 50; + } + + i++; + } + + if (selectedMission->available) + { + drawText(525, 135, 18, TA_LEFT, colors.lightGrey, "Pilot: %s", selectedMission->pilot); + drawText(525, 160, 18, TA_LEFT, colors.lightGrey, "Craft: %s", selectedMission->craft); + drawText(525, 185, 18, TA_LEFT, colors.lightGrey, "Squadron: %s", selectedMission->squadron); + + limitTextWidth(500); + drawText(525, 230, 22, TA_LEFT, colors.white, selectedMission->description); + limitTextWidth(0); + } + else + { + drawText(525, 135, 18, TA_LEFT, colors.lightGrey, "Pilot: -"); + drawText(525, 160, 18, TA_LEFT, colors.lightGrey, "Craft: -"); + drawText(525, 185, 18, TA_LEFT, colors.lightGrey, "Squadron: -"); + + limitTextWidth(500); + drawText(525, 230, 22, TA_LEFT, colors.darkGrey, "You cannot play this mission yet. Complete the previous mission to unlock."); + limitTextWidth(0); + } + + if (selectedMission && selectedMission->available && selectedMission->challengeHead.next) + { + y = SCREEN_HEIGHT - 100; + + for (challenge = selectedMission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + y -= 25; + } + + drawText(525, y, 22, TA_LEFT, colors.yellow, "Challenges"); + + y += 30; + + for (challenge = selectedMission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + drawText(525, y, 18, TA_LEFT, challenge->passed ? colors.green : colors.lightGrey, "%s%s", getChallengeDescription(challenge), challenge->passed ? " - COMPLETE" : ""); + + y += 25; + } + } +} + +static void handleKeyboard(void) +{ + if (show == SHOW_GALAXY) + { + if (viewingSystem) + { + handleSelectedSystemKB(); + } + else + { + handleGalaxyKB(); + } + } + else if (app.keyboard[SDL_SCANCODE_ESCAPE]) + { + switch (show) + { + case SHOW_MENU: + show = SHOW_GALAXY; + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + break; + + case SHOW_OPTIONS: + case SHOW_STATS: + show = SHOW_MENU; + selectWidget("resume", "galacticMap"); + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + break; + } + + playSound(SND_GUI_CLOSE); + } +} + +static void handleGalaxyKB(void) +{ + int lastX, lastY; + + lastX = camera.x; + lastY = camera.y; + + ssx = ssy = 0; + + if (app.keyboard[SDL_SCANCODE_LEFT]) + { + ssx = -1; + camera.x -= CAMERA_SPEED; + } + + if (app.keyboard[SDL_SCANCODE_RIGHT]) + { + ssx = 1; + camera.x += CAMERA_SPEED; + } + + if (app.keyboard[SDL_SCANCODE_UP]) + { + ssy = -1; + camera.y -= CAMERA_SPEED; + } + + if (app.keyboard[SDL_SCANCODE_DOWN]) + { + ssy = 1; + camera.y += CAMERA_SPEED; + } + + if (app.keyboard[SDL_SCANCODE_RETURN]) + { + selectStarSystem(); + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + } + + camera.x = MAX(-800, MIN(camera.x, 2464)); + camera.y = MAX(-475, MIN(camera.y, 1235)); + + if (lastX == camera.x) + { + ssx = 0; + } + + if (lastY == camera.y) + { + ssy = 0; + } + + if (app.keyboard[SDL_SCANCODE_ESCAPE]) + { + selectWidget("resume", "galacticMap"); + show = SHOW_MENU; + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + playSound(SND_GUI_CLOSE); + } +} + +static void handleSelectedSystemKB(void) +{ + if (app.keyboard[SDL_SCANCODE_UP]) + { + prevMission(); + } + + if (app.keyboard[SDL_SCANCODE_DOWN]) + { + nextMission(); + } + + if (app.keyboard[SDL_SCANCODE_RETURN] && selectedMission->available) + { + if (selectedMission->available) + { + playSound(SND_GUI_SELECT); + + initBattle(); + game.currentMission = selectedMission; + loadMission(selectedMission->filename); + } + else + { + playSound(SND_GUI_DENIED); + } + } + + if (app.keyboard[SDL_SCANCODE_ESCAPE]) + { + viewingSystem = 0; + + playSound(SND_GUI_CLOSE); + } + + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); +} + +static void prevMission(void) +{ + selectedMissionIndex = MAX(0, selectedMissionIndex - 1); + + if (selectedMissionIndex <= missionListStart + 3) + { + missionListStart = MAX(0, missionListStart - 1); + } + + selectedMission = NULL; +} + +static void nextMission(void) +{ + selectedMissionIndex = MIN(selectedMissionIndex + 1, selectedStarSystem->totalMissions - 1); + + if (selectedMissionIndex >= missionListStart + 5) + { + missionListStart = MIN(missionListStart + 1, selectedStarSystem->totalMissions - 9); + } + + selectedMission = NULL; +} + +static void drawMenu(void) +{ + SDL_Rect r; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); + SDL_RenderFillRect(app.renderer, NULL); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + r.w = 400; + r.h = 400; + r.x = (SCREEN_WIDTH / 2) - r.w / 2; + r.y = (SCREEN_HEIGHT / 2) - r.h / 2; + + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255); + SDL_RenderDrawRect(app.renderer, &r); + + drawWidgets("galacticMap"); +} + +static void resume(void) +{ + show = SHOW_GALAXY; +} + +static void options(void) +{ + show = SHOW_OPTIONS; + + initOptions(returnFromOptions); +} + +static void stats(void) +{ + selectWidget("ok", "stats"); + + show = SHOW_STATS; +} + +static void statsOK(void) +{ + selectWidget("resume", "galacticMap"); + + show = SHOW_MENU; +} + +static void returnFromOptions(void) +{ + show = SHOW_MENU; + + selectWidget("resume", "galacticMap"); +} + +static void quit(void) +{ + initTitle(); +} + +void destroyGalacticMap(void) +{ + Pulse *pulse; + + while (pulseHead.next) + { + pulse = pulseHead.next; + pulseHead.next = pulse->next; + free(pulse); + } + + pulseTail = &pulseHead; +} diff --git a/src/galaxy/galacticMap.h b/src/galaxy/galacticMap.h new file mode 100644 index 0000000..90c9965 --- /dev/null +++ b/src/galaxy/galacticMap.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +#define CAMERA_SPEED 8 + +#define SHOW_GALAXY 0 +#define SHOW_MENU 1 +#define SHOW_OPTIONS 2 +#define SHOW_STATS 3 + +extern void prepareScene(void); +extern void presentScene(void); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern void initBattle(void); +extern void loadMission(char *filename); +extern SDL_Texture *getTexture(char *filename); +extern void startSectionTransition(void); +extern void endSectionTransition(void); +extern void saveGame(void); +extern char *getChallengeDescription(Challenge *c); +extern void blit(SDL_Texture *t, int x, int y, int centered); +extern int collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); +extern void updateStarSystemDescriptions(void); +extern void drawCircle(int cx, int cy, int radius, int r, int g, int b, int a); +extern void playMusic(char *filename); +extern void stopMusic(void); +extern void doStars(float dx, float dy); +extern void drawStars(void); +extern void limitTextWidth(int width); +extern void drawBackground(SDL_Texture *texture); +extern void initBackground(void); +extern void scrollBackground(float x, float y); +extern void drawWidgets(char *groupName); +extern void doWidgets(void); +extern Widget *getWidget(const char *name, const char *group); +extern void selectWidget(const char *name, const char *group); +extern void initTitle(void); +extern void drawOptions(void); +extern void initOptions(void (*returnFromOptions)(void)); +extern void drawStats(void); +extern void playSound(int id); + +extern App app; +extern Colors colors; +extern Game game; diff --git a/src/galaxy/mission.c b/src/galaxy/mission.c new file mode 100644 index 0000000..102ff33 --- /dev/null +++ b/src/galaxy/mission.c @@ -0,0 +1,199 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "mission.h" + +static void loadObjectives(cJSON *node); +static void loadPlayer(cJSON *node); +static void loadFighters(cJSON *node); +static void loadFighterGroups(cJSON *node); +static unsigned long hashcode(const char *str); + +void loadMission(char *filename) +{ + cJSON *root; + char *text, music[MAX_NAME_LENGTH]; + + startSectionTransition(); + + stopMusic(); + + text = readFile(filename); + + srand(hashcode(filename)); + + root = cJSON_Parse(text); + + battle.background = getTexture(cJSON_GetObjectItem(root, "background")->valuestring); + battle.planetTexture = getTexture(cJSON_GetObjectItem(root, "planet")->valuestring); + battle.planet.x = rand() % SCREEN_WIDTH - rand() % SCREEN_WIDTH; + battle.planet.y = rand() % SCREEN_HEIGHT - rand() % SCREEN_HEIGHT; + + loadObjectives(cJSON_GetObjectItem(root, "objectives")); + + loadPlayer(cJSON_GetObjectItem(root, "player")); + + loadFighters(cJSON_GetObjectItem(root, "fighters")); + + loadFighterGroups(cJSON_GetObjectItem(root, "fighterGroups")); + + STRNCPY(music, cJSON_GetObjectItem(root, "music")->valuestring, MAX_NAME_LENGTH); + + cJSON_Delete(root); + free(text); + + srand(time(NULL)); + + endSectionTransition(); + + if (!battle.objectiveHead.next) + { + battle.status = MS_IN_PROGRESS; + } + + playMusic(music); +} + +static void loadObjectives(cJSON *node) +{ + Objective *o; + + if (node) + { + node = node->child; + + while (node) + { + o = malloc(sizeof(Objective)); + memset(o, 0, sizeof(Objective)); + + STRNCPY(o->description, cJSON_GetObjectItem(node, "description")->valuestring, MAX_DESCRIPTION_LENGTH); + STRNCPY(o->targetName, cJSON_GetObjectItem(node, "targetName")->valuestring, MAX_NAME_LENGTH); + o->targetValue = cJSON_GetObjectItem(node, "targetValue")->valueint; + + battle.objectiveTail->next = o; + battle.objectiveTail = o; + + node = node->next; + } + } +} + +static void loadPlayer(cJSON *node) +{ + char *type; + int side; + + type = cJSON_GetObjectItem(node, "type")->valuestring; + side = lookup(cJSON_GetObjectItem(node, "side")->valuestring); + + player = spawnFighter(type, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, side); + player->defaultAction = NULL; +} + +static void loadFighters(cJSON *node) +{ + Fighter *f; + char *type; + int side, x, y; + + if (node) + { + node = node->child; + + while (node) + { + type = cJSON_GetObjectItem(node, "type")->valuestring; + side = lookup(cJSON_GetObjectItem(node, "side")->valuestring); + x = cJSON_GetObjectItem(node, "x")->valueint; + y = cJSON_GetObjectItem(node, "y")->valueint; + + f = spawnFighter(type, x, y, side); + + STRNCPY(f->name, cJSON_GetObjectItem(node, "name")->valuestring, MAX_NAME_LENGTH); + + node = node->next; + } + } +} + +static void loadFighterGroups(cJSON *node) +{ + Fighter *f; + char *type, *name; + int side, x, y; + int number, i; + + if (node) + { + node = node->child; + + while (node) + { + type = cJSON_GetObjectItem(node, "type")->valuestring; + side = lookup(cJSON_GetObjectItem(node, "side")->valuestring); + number = cJSON_GetObjectItem(node, "number")->valueint; + x = cJSON_GetObjectItem(node, "x")->valueint; + y = cJSON_GetObjectItem(node, "y")->valueint; + name = cJSON_GetObjectItem(node, "name")->valuestring; + + for (i = 0 ; i < number ; i++) + { + f = spawnFighter(type, x, y, side); + + STRNCPY(f->name, name, MAX_NAME_LENGTH); + } + + node = node->next; + } + } +} + +Mission *getMission(StarSystem *starSystem, char *filename) +{ + Mission *mission; + + for (mission = starSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + if (strcmp(mission->filename, filename) == 0) + { + return mission; + } + } + + return NULL; +} + +static unsigned long hashcode(const char *str) +{ + unsigned long hash = 5381; + int c; + + c = *str; + + while (c) + { + hash = ((hash << 5) + hash) + c; + + c = *str++; + } + + return abs(hash); +} diff --git a/src/galaxy/mission.h b/src/galaxy/mission.h new file mode 100644 index 0000000..802cbfc --- /dev/null +++ b/src/galaxy/mission.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "time.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern long lookup(char *name); +extern char *readFile(char *filename); +extern SDL_Texture *getTexture(char *filename); +extern Fighter *spawnFighter(char *name, int x, int y, int side); +extern void startSectionTransition(void); +extern void endSectionTransition(void); +extern void playMusic(char *filename); +extern void stopMusic(void); + +extern Battle battle; +extern Fighter *player; diff --git a/src/galaxy/starSystems.c b/src/galaxy/starSystems.c new file mode 100644 index 0000000..4dfd43f --- /dev/null +++ b/src/galaxy/starSystems.c @@ -0,0 +1,196 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "starSystems.h" + +static void loadStarSystem(cJSON *starSystemJSON); +static void loadMissionMeta(char *filename, StarSystem *starSystem); + +void initStarSystems(void) +{ + cJSON *root, *node; + char *text; + + text = readFile("data/galaxy/starSystems.json"); + root = cJSON_Parse(text); + + for (node = cJSON_GetObjectItem(root, "starSystems")->child ; node != NULL ; node = node->next) + { + loadStarSystem(node); + } + + cJSON_Delete(root); + free(text); +} + +static void loadStarSystem(cJSON *starSystemJSON) +{ + cJSON *node; + StarSystem *starSystem; + + starSystem = malloc(sizeof(StarSystem)); + memset(starSystem, 0, sizeof(StarSystem)); + game.starSystemTail->next = starSystem; + game.starSystemTail = starSystem; + + STRNCPY(starSystem->name, cJSON_GetObjectItem(starSystemJSON, "name")->valuestring, MAX_NAME_LENGTH); + starSystem->side = lookup(cJSON_GetObjectItem(starSystemJSON, "side")->valuestring); + starSystem->x = cJSON_GetObjectItem(starSystemJSON, "x")->valueint; + starSystem->y = cJSON_GetObjectItem(starSystemJSON, "y")->valueint; + + starSystem->missionHead.completed = 1; + starSystem->missionTail = &starSystem->missionHead; + + for (node = cJSON_GetObjectItem(starSystemJSON, "missions")->child ; node != NULL ; node = node->next) + { + loadMissionMeta(node->valuestring, starSystem); + } + + starSystem->x *= 3; + starSystem->y *= 3; +} + +static void loadMissionMeta(char *filename, StarSystem *starSystem) +{ + Mission *mission; + Challenge *challenge, *challengeTail; + cJSON *root, *node; + char *text; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", filename); + + text = readFile(filename); + + root = cJSON_Parse(text); + + mission = malloc(sizeof(Mission)); + memset(mission, 0, sizeof(Mission)); + starSystem->missionTail->next = mission; + starSystem->missionTail = mission; + + STRNCPY(mission->name, cJSON_GetObjectItem(root, "name")->valuestring, MAX_NAME_LENGTH); + STRNCPY(mission->description, cJSON_GetObjectItem(root, "description")->valuestring, MAX_DESCRIPTION_LENGTH); + STRNCPY(mission->filename, filename, MAX_DESCRIPTION_LENGTH); + + node = cJSON_GetObjectItem(root, "player"); + + if (node) + { + STRNCPY(mission->pilot, cJSON_GetObjectItem(node, "pilot")->valuestring, MAX_NAME_LENGTH); + STRNCPY(mission->squadron, cJSON_GetObjectItem(node, "squadron")->valuestring, MAX_NAME_LENGTH); + STRNCPY(mission->craft, cJSON_GetObjectItem(node, "type")->valuestring, MAX_NAME_LENGTH); + } + + challengeTail = &mission->challengeHead; + + node = cJSON_GetObjectItem(root, "challenges"); + + if (node) + { + node = node->child; + + while (node) + { + challenge = malloc(sizeof(Challenge)); + memset(challenge, 0, sizeof(Challenge)); + + challenge->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + challenge->targetValue = cJSON_GetObjectItem(node, "targetValue")->valueint; + + challengeTail->next = challenge; + challengeTail = challenge; + + node = node->next; + } + } + + cJSON_Delete(root); + free(text); +} + +StarSystem *getStarSystem(char *name) +{ + StarSystem *starSystem; + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + if (strcmp(starSystem->name, name) == 0) + { + return starSystem; + } + } + + return NULL; +} + +void updateStarSystemDescriptions(void) +{ + StarSystem *starSystem; + Mission *mission; + Challenge *challenge; + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + starSystem->completedMissions = starSystem->totalMissions = starSystem->completedChallenges = starSystem->totalChallenges = 0; + + for (mission = starSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + starSystem->totalMissions++; + + if (mission->completed) + { + starSystem->completedMissions++; + } + + for (challenge = mission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + starSystem->totalChallenges++; + + if (challenge->passed) + { + starSystem->completedChallenges++; + } + } + } + + sprintf(starSystem->description, "[ %s ] [ Missions %d / %d ] [ Challenges %d / %d ]", starSystem->name, starSystem->completedMissions, starSystem->totalMissions, starSystem->completedChallenges, starSystem->totalChallenges); + } +} + +void destroyStarSystems(void) +{ + StarSystem *starSystem; + Mission *mission; + + while (game.starSystemHead.next) + { + starSystem = game.starSystemHead.next; + + while (starSystem->missionHead.next) + { + mission = starSystem->missionHead.next; + starSystem->missionHead.next = mission->next; + free(mission); + } + + game.starSystemHead.next = starSystem->next; + free(starSystem); + } +} diff --git a/src/galaxy/starSystems.h b/src/galaxy/starSystems.h new file mode 100644 index 0000000..48a53b0 --- /dev/null +++ b/src/galaxy/starSystems.h @@ -0,0 +1,30 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern char *readFile(char *filename); +extern long lookup(char *name); + +extern Game game; diff --git a/src/galaxy/stats.c b/src/galaxy/stats.c new file mode 100644 index 0000000..e7a4da4 --- /dev/null +++ b/src/galaxy/stats.c @@ -0,0 +1,102 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "stats.h" + +void drawStats(void) +{ + int y, hours, minutes, seconds; + SDL_Rect r; + char timePlayed[MAX_NAME_LENGTH]; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); + SDL_RenderFillRect(app.renderer, NULL); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + r.w = 500; + r.h = 600; + r.x = (SCREEN_WIDTH / 2) - r.w / 2; + r.y = (SCREEN_HEIGHT / 2) - r.h / 2; + + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255); + SDL_RenderDrawRect(app.renderer, &r); + + drawText(SCREEN_WIDTH / 2, 70, 28, TA_CENTER, colors.white, "Stats"); + + SDL_SetRenderDrawColor(app.renderer, 128, 128, 128, 255); + SDL_RenderDrawLine(app.renderer, r.x, 120, r.x + r.w, 120); + + y = 140; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Missions Started"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.missionsStarted); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Missions Completed"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.missionsCompleted); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Shots Fired"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.shotsFired); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Shots Hit"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.shotsHit); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Missiles Fired"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.missilesFired); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Missiles Hit"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.missilesHit); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Enemies Killed (Player)"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.playerKills); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Enemies Killed (All)"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.enemiesKilled); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Allies Lost"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.alliesKilled); + y += 40; + + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Times Killed"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, "%d", game.stats.playerKilled); + y += 40; + + seconds = game.stats.time / FPS; + minutes = (seconds / 60) % 60; + hours = seconds / (60 * 60); + + seconds %= 60; + + sprintf(timePlayed, "%dh:%02dm:%02ds", hours, minutes, seconds); + drawText(r.x + 20, y, 18, TA_LEFT, colors.white, "Time Played"); + drawText(r.x + r.w - 20, y, 18, TA_RIGHT, colors.white, timePlayed); + + drawWidgets("stats"); +} diff --git a/src/galaxy/stats.h b/src/galaxy/stats.h new file mode 100644 index 0000000..2639869 --- /dev/null +++ b/src/galaxy/stats.h @@ -0,0 +1,33 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void selectWidget(const char *name, const char *group); +extern Widget *getWidget(const char *name, const char *group); +extern void drawWidgets(char *groupName); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); + +extern App app; +extern Colors colors; +extern Game game; diff --git a/src/game/game.c b/src/game/game.c new file mode 100644 index 0000000..622df15 --- /dev/null +++ b/src/game/game.c @@ -0,0 +1,54 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "game.h" + +void initGame(void) +{ + memset(&game, 0, sizeof(Game)); + + game.starSystemTail = &game.starSystemHead; +} + +void resetGame(void) +{ + StarSystem *starSystem; + Mission *mission; + Challenge *challenge; + + memset(&game.stats, 0, sizeof(Stats)); + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + for (mission = starSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + mission->completed = 0; + + for (challenge = mission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + challenge->passed = 0; + } + } + } +} + +void destroyGame(void) +{ +} diff --git a/src/game/game.h b/src/game/game.h new file mode 100644 index 0000000..56aae6b --- /dev/null +++ b/src/game/game.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern Game game; diff --git a/src/game/title.c b/src/game/title.c new file mode 100644 index 0000000..3d17a92 --- /dev/null +++ b/src/game/title.c @@ -0,0 +1,235 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "title.h" + +static void logic(void); +static void draw(void); +static void handleKeyboard(void); +static void initFighters(void); +static void doFighters(void); +static void drawFighters(void); +static void newGame(void); +static void continueGame(void); +static void options(void); +static void quit(void); +static void returnFromOptions(void); + +static SDL_Texture *background; +static SDL_Texture *logo; +static SDL_Texture *pandoranWar; +static SDL_Texture *earthTexture; +static PointF earth; +static Fighter fighters[NUM_FIGHTERS]; +static const char *fighterTextures[] = {"gfx/fighters/firefly.png", "gfx/fighters/hammerhead.png", "gfx/fighters/hyena.png", "gfx/fighters/khepri.png", "gfx/fighters/kingfisher.png", "gfx/fighters/leopard.png", "gfx/fighters/nymph.png", "gfx/fighters/ray.png", "gfx/fighters/rook.png", "gfx/fighters/taf.png"}; +static int showingOptions; + +void initTitle(void) +{ + startSectionTransition(); + + stopMusic(); + + app.delegate.logic = &logic; + app.delegate.draw = &draw; + memset(&app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + battle.ssx = battle.ssy = 0; + + destroyBattle(); + + logo = getTexture("gfx/title/logo.png"); + + pandoranWar = getTexture("gfx/title/pandoran.png"); + + background = getTexture("gfx/backgrounds/background02.jpg"); + + earthTexture = getTexture("gfx/planets/earth.png"); + + earth.x = rand() % SCREEN_WIDTH; + earth.y = -(128 + (rand() % 128)); + + initBackground(); + + initFighters(); + + selectWidget("newGame", "title"); + getWidget("newGame", "title")->action = newGame; + getWidget("continue", "title")->action = continueGame; + getWidget("options", "title")->action = options; + getWidget("quit", "title")->action = quit; + + showingOptions = 0; + + endSectionTransition(); + + playMusic("music/Rise of spirit.ogg"); +} + +static void initFighters(void) +{ + int i, numTextures; + + numTextures = sizeof(fighterTextures) / sizeof(char*); + + memset(&fighters, 0, sizeof(Fighter) * NUM_FIGHTERS); + + for (i = 0 ; i < NUM_FIGHTERS ; i++) + { + fighters[i].x = rand() % (SCREEN_WIDTH - 64); + fighters[i].y = SCREEN_HEIGHT + (rand() % SCREEN_HEIGHT); + fighters[i].texture = getTexture(fighterTextures[rand() % numTextures]); + fighters[i].dy = -(1 + rand() % 3); + } +} + +static void logic(void) +{ + handleKeyboard(); + + scrollBackground(0, 0.25); + + doStars(0, -0.5); + + earth.y += 0.1; + + if (earth.y > SCREEN_HEIGHT + 128) + { + earth.x = rand() % SCREEN_WIDTH; + earth.y = -(128 + (rand() % 128)); + } + + doFighters(); + + doEffects(); + + doWidgets(); +} + +static void doFighters(void) +{ + int i; + + for (i = 0 ; i < NUM_FIGHTERS ; i++) + { + fighters[i].y += fighters[i].dy; + + self = &fighters[i]; + + addEngineEffect(); + + if (fighters[i].y <= -64) + { + fighters[i].x = rand() % (SCREEN_WIDTH - 64); + fighters[i].y = SCREEN_HEIGHT + (rand() % SCREEN_HEIGHT); + } + } +} + +static void draw(void) +{ + prepareScene(); + + drawBackground(background); + + drawStars(); + + blit(earthTexture, earth.x, earth.y, 1); + + drawFighters(); + + drawEffects(); + + blit(logo, SCREEN_WIDTH / 2, 50, 1); + + blit(pandoranWar, SCREEN_WIDTH / 2, 110, 1); + + drawText(10, SCREEN_HEIGHT - 25, 14, TA_LEFT, colors.white, "Copyright Parallel Realities, 2015"); + drawText(SCREEN_WIDTH - 10, SCREEN_HEIGHT - 25, 14, TA_RIGHT, colors.white, "Version %.2f-%d", VERSION, RELEASE); + + if (!showingOptions) + { + drawWidgets("title"); + } + else + { + drawOptions(); + } + + presentScene(); +} + +static void drawFighters(void) +{ + int i; + + for (i = 0 ; i < NUM_FIGHTERS ; i++) + { + blit(fighters[i].texture, fighters[i].x, fighters[i].y, 1); + } +} + +static void handleKeyboard(void) +{ + if (app.keyboard[SDL_SCANCODE_ESCAPE]) + { + returnFromOptions(); + + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); + + playSound(SND_GUI_CLOSE); + } +} + +static void newGame(void) +{ + resetGame(); + + initGalacticMap(); +} + +static void continueGame(void) +{ + if (fileExists(getSaveFilePath("game.save"))) + { + loadGame(); + } + + initGalacticMap(); +} + +static void options(void) +{ + showingOptions = 1; + + initOptions(returnFromOptions); +} + +static void returnFromOptions(void) +{ + showingOptions = 0; + + selectWidget("newGame", "title"); +} + +static void quit(void) +{ + exit(1); +} diff --git a/src/game/title.h b/src/game/title.h new file mode 100644 index 0000000..77e2535 --- /dev/null +++ b/src/game/title.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +#define NUM_FIGHTERS 12 + +extern void prepareScene(void); +extern void presentScene(void); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern SDL_Texture *getTexture(const char *filename); +extern void startSectionTransition(void); +extern void endSectionTransition(void); +extern void blit(SDL_Texture *t, int x, int y, int centered); +extern void stopMusic(void); +extern void doStars(float dx, float dy); +extern void drawStars(void); +extern void drawBackground(SDL_Texture *texture); +extern void initBackground(void); +extern void scrollBackground(float x, float y); +extern void addEngineEffect(void); +extern void doEffects(void); +extern void drawEffects(void); +extern void drawWidgets(char *groupName); +extern void doWidgets(void); +extern Widget *getWidget(const char *name, const char *group); +extern void selectWidget(const char *name, const char *group); +extern void loadGame(void); +extern int fileExists(char *filename); +extern void resetGame(void); +extern void initGalacticMap(void); +extern void initOptions(void (*returnFromOptions)(void)); +extern void drawOptions(void); +extern char *getSaveFilePath(char *filename); +extern void playMusic(char *filename); +extern void destroyBattle(void); +extern void playSound(int id); + +extern App app; +extern Battle battle; +extern Colors colors; +extern Game game; +extern Fighter *self; diff --git a/src/json/cJSON.c b/src/json/cJSON.c new file mode 100644 index 0000000..46cbf72 --- /dev/null +++ b/src/json/cJSON.c @@ -0,0 +1,750 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } + +typedef struct {char *buffer; int length; int offset; } printbuffer; + +static char* ensure(printbuffer *p,int needed) +{ + char *newbuffer;int newsize; + if (!p || !p->buffer) return 0; + needed+=p->offset; + if (needed<=p->length) return p->buffer+p->offset; + + newsize=pow2gt(needed); + newbuffer=(char*)cJSON_malloc(newsize); + if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} + if (newbuffer) memcpy(newbuffer,p->buffer,p->length); + cJSON_free(p->buffer); + p->length=newsize; + p->buffer=newbuffer; + return newbuffer+p->offset; +} + +static int update(printbuffer *p) +{ + char *str; + if (!p || !p->buffer) return 0; + str=p->buffer+p->offset; + return p->offset+strlen(str); +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item,printbuffer *p) +{ + char *str=0; + double d=item->valuedouble; + if (d==0) + { + if (p) str=ensure(p,2); + else str=(char*)cJSON_malloc(2); /* special case for 0. */ + if (str) strcpy(str,"0"); + } + else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + if (p) str=ensure(p,21); + else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + if (p) str=ensure(p,64); + else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str,printbuffer *p) +{ + const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; + + for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; + if (!flag) + { + len=ptr-str; + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + ptr2=out;*ptr2++='\"'; + strcpy(ptr2,str); + ptr2[len]='\"'; + ptr2[len+1]=0; + return out; + } + + if (!str) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (!out) return 0; + strcpy(out,"\"\""); + return out; + } + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/Fighter/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} + +char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) +{ + printbuffer p; + p.buffer=(char*)cJSON_malloc(prebuffer); + p.length=prebuffer; + p.offset=0; + return print_value(item,0,fmt,&p); + return p.buffer; +} + + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char *out=0; + if (!item) return 0; + if (p) + { + switch ((item->type)&255) + { + case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} + case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} + case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} + case cJSON_Number: out=print_number(item,p);break; + case cJSON_String: out=print_string(item,p);break; + case cJSON_Array: out=print_array(item,depth,fmt,p);break; + case cJSON_Object: out=print_object(item,depth,fmt,p);break; + } + } + else + { + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item,0);break; + case cJSON_String: out=print_string(item,0);break; + case cJSON_Array: out=print_array(item,depth,fmt,0);break; + case cJSON_Object: out=print_object(item,depth,fmt,0);break; + } + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + size_t tmplen=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + + if (p) + { + /* Compose the output array. */ + i=p->offset; + ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; + child=item->child; + while (child && !fail) + { + print_value(child,depth+1,fmt,p); + p->offset=update(p); + if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} + child=child->next; + } + ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; + out=(p->buffer)+i; + } + else + { + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt,0); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + size_t tmplen=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + if (p) out=ensure(p,fmt?depth+4:3); + else out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ioffset; + len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; + *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; + child=item->child;depth++; + while (child) + { + if (fmt) + { + ptr=ensure(p,depth); if (!ptr) return 0; + for (j=0;joffset+=depth; + } + print_string_ptr(child->string,p); + p->offset=update(p); + + len=fmt?2:1; + ptr=ensure(p,len); if (!ptr) return 0; + *ptr++=':';if (fmt) *ptr++='\t'; + p->offset+=len; + + print_value(child,depth,fmt,p); + p->offset=update(p); + + len=(fmt?1:0)+(child->next?1:0); + ptr=ensure(p,len+1); if (!ptr) return 0; + if (child->next) *ptr++=','; + if (fmt) *ptr++='\n';*ptr=0; + p->offset+=len; + child=child->next; + } + ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; + if (fmt) for (i=0;ibuffer)+i; + } + else + { + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string,0); + entries[i++]=ret=print_value(child,depth,fmt,0); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} + newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *json) +{ + char *into=json; + while (*json) + { + if (*json==' ') json++; + else if (*json=='\t') json++; /* Whitespace characters. */ + else if (*json=='\r') json++; + else if (*json=='\n') json++; + else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ + else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ + else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ + else *into++=*json++; /* All other characters. */ + } + *into=0; /* and null-terminate. */ +} diff --git a/src/json/cJSON.h b/src/json/cJSON.h new file mode 100644 index 0000000..ce38cf2 --- /dev/null +++ b/src/json/cJSON.h @@ -0,0 +1,149 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON Fighter to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON Fighter to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Render a cJSON Fighter to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); +/* Delete a cJSON Fighter and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3c53a13 --- /dev/null +++ b/src/main.c @@ -0,0 +1,111 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "main.h" + +int main(int argc, char *argv[]) +{ + float td; + long then, lastFrameTime, frames; + + SDL_Event event; + + memset(&app, 0, sizeof(App)); + + atexit(cleanup); + + srand(time(NULL)); + + setlocale(LC_NUMERIC, ""); + + initSDL(); + + initGameSystem(); + + if (argc > 1) + { + initBattle(); + loadMission(argv[1]); + } + else + { + initTitle(); + } + + app.fps = frames = td = 0; + then = SDL_GetTicks(); + lastFrameTime = SDL_GetTicks() + 1000; + + while (1) + { + td += (SDL_GetTicks() - then); + + then = SDL_GetTicks(); + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + if (event.key.keysym.scancode >= 0 && event.key.keysym.scancode < MAX_KEYBOARD_KEYS) + { + app.keyboard[event.key.keysym.scancode] = 1; + } + break; + + case SDL_KEYUP: + if (event.key.keysym.scancode >= 0 && event.key.keysym.scancode < MAX_KEYBOARD_KEYS) + { + app.keyboard[event.key.keysym.scancode] = 0; + } + break; + + case SDL_QUIT: + exit(0); + break; + } + } + + while (td >= LOGIC_RATE) + { + app.delegate.logic(); + td -= LOGIC_RATE; + + game.stats.time++; + } + + app.delegate.draw(); + + frames++; + + if (SDL_GetTicks() > lastFrameTime) + { + app.fps = frames; + frames = 0; + lastFrameTime = SDL_GetTicks() + 1000; + + /*saveScreenshot();*/ + } + + SDL_Delay(1); + } + + return 0; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..559a93e --- /dev/null +++ b/src/main.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "time.h" +#include "locale.h" + +#include "defs.h" +#include "structs.h" + +extern void cleanup(void); +extern void initSDL(void); +extern void initBattle(void); +extern void initGameSystem(void); +extern void initTitle(void); +extern void loadMission(char *filename); +extern void saveScreenshot(void); + +App app; +Colors colors; +Battle battle; +Fighter *self; +Fighter *player; +Game game; diff --git a/src/structs.h b/src/structs.h new file mode 100644 index 0000000..360ab1b --- /dev/null +++ b/src/structs.h @@ -0,0 +1,289 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +typedef struct SDL_Texture SDL_Texture; +typedef struct Texture Texture; +typedef struct Lookup Lookup; +typedef struct Weapon Weapon; +typedef struct Fighter Fighter; +typedef struct Bullet Bullet; +typedef struct Effect Effect; +typedef struct Objective Objective; +typedef struct StarSystem StarSystem; +typedef struct Challenge Challenge; +typedef struct Mission Mission; +typedef struct Pulse Pulse; +typedef struct Widget Widget; +typedef struct HudMessage HudMessage; + +typedef struct { + float x; + float y; +} PointF; + +struct Texture { + char name[MAX_NAME_LENGTH]; + long hash; + SDL_Texture *texture; + Texture *next; +}; + +typedef struct { + void (*logic)(void); + void (*draw)(void); + void (*handleClick)(int x, int y, int btn); + void (*handleDrag)(int x, int y, int dx, int dy, int cx, int cy); + void (*handleMouseUp)(int x, int y, int btn); +} Delegate; + +struct Lookup { + char name[MAX_NAME_LENGTH]; + long value; + Lookup *next; +}; + +struct Weapon { + int type; + int ammo; + int maxAmmo; + int x; + int y; +}; + +struct Fighter { + char name[MAX_NAME_LENGTH]; + int active; + int side; + float x; + float y; + float dx; + float dy; + float thrust; + int speed; + int angle; + int alive; + int health; + int maxHealth; + int shield; + int maxShield; + int reload; + int reloadTime; + int shieldRecharge; + int shieldRechargeRate; + int armourHit; + int shieldHit; + int thinkTime; + int aiActionTime; + int aggression; + int separationRadius; + Weapon guns[MAX_FIGHTER_GUNS]; + Weapon missiles; + long flags; + Fighter *target; + void (*action)(void); + void (*defaultAction)(void); + void (*die)(void); + SDL_Texture *texture; + Fighter *next; +}; + +struct Bullet { + int type; + float x; + float y; + float dx; + float dy; + int sound; + int life; + int damage; + int angle; + long flags; + SDL_Texture *texture; + Fighter *owner; + Fighter *target; + Bullet *next; +}; + +typedef struct { + float x; + float y; + float speed; +} Star; + +struct Pulse { + int x; + int y; + float size; + int life; + int r, g, b; + Pulse *next; +}; + +struct Effect { + int type; + float x; + float y; + float dx; + float dy; + float health; + int size; + int r; + int g; + int b; + int a; + SDL_Texture *texture; + Effect *next; +}; + +struct Objective { + char description[MAX_DESCRIPTION_LENGTH]; + char targetName[MAX_NAME_LENGTH]; + int currentValue; + int targetValue; + int status; + Objective *next; +}; + +struct Challenge { + int type; + int targetValue; + int passed; + Challenge *next; +}; + +struct Mission { + char name[MAX_NAME_LENGTH]; + char description[MAX_DESCRIPTION_LENGTH]; + char filename[MAX_DESCRIPTION_LENGTH]; + char pilot[MAX_NAME_LENGTH]; + char squadron[MAX_NAME_LENGTH]; + char craft[MAX_NAME_LENGTH]; + int available; + int completed; + Challenge challengeHead; + Mission *next; +}; + +struct StarSystem { + char name[MAX_NAME_LENGTH]; + char description[MAX_DESCRIPTION_LENGTH]; + int side; + int x; + int y; + int completedMissions; + int totalMissions; + int completedChallenges; + int totalChallenges; + Mission missionHead, *missionTail; + StarSystem *next; +}; + +typedef struct { + int missionsStarted; + int missionsCompleted; + int shotsFired; + int shotsHit; + int missilesFired; + int missilesHit; + int enemiesKilled; + int alliesKilled; + int playerKilled; + int playerKills; + unsigned int time; +} Stats; + +typedef struct { + float ssx; + float ssy; + int numAllies; + int numEnemies; + int status; + int missionFinishedTimer; + int numObjectivesComplete, numObjectivesTotal; + SDL_Texture *background, *planetTexture; + PointF planet; + Fighter fighterHead, *fighterTail; + Bullet bulletHead, *bulletTail; + Effect effectHead, *effectTail; + Objective objectiveHead, *objectiveTail; + Stats stats; +} Battle; + +typedef struct { + StarSystem starSystemHead, *starSystemTail; + Mission *currentMission; + Stats stats; +} Game; + +struct Widget { + char name[MAX_NAME_LENGTH]; + char group[MAX_NAME_LENGTH]; + int type; + int value; + char text[MAX_NAME_LENGTH]; + char **options; + int numOptions; + int currentOption; + int visible; + int enabled; + SDL_Rect rect; + void (*action)(void); + void (*onChange)(char *value); + Widget *next; +}; + +struct HudMessage { + char message[MAX_DESCRIPTION_LENGTH]; + SDL_Color color; + int life; + HudMessage *next; +}; + +typedef struct { + char saveDir[MAX_FILENAME_LENGTH]; + int winWidth; + int winHeight; + float scaleX; + float scaleY; + int fullscreen; + int musicVolume; + int soundVolume; + int vSync; + int fps; + int keyboard[MAX_KEYBOARD_KEYS]; + SDL_Texture *backBuffer; + SDL_Renderer *renderer; + SDL_Window *window; + Delegate delegate; +} App; + +typedef struct { + SDL_Color red; + SDL_Color orange; + SDL_Color yellow; + SDL_Color green; + SDL_Color blue; + SDL_Color cyan; + SDL_Color purple; + SDL_Color white; + SDL_Color black; + SDL_Color lightGrey; + SDL_Color darkGrey; +} Colors; diff --git a/src/system/init.c b/src/system/init.c new file mode 100644 index 0000000..b9af67a --- /dev/null +++ b/src/system/init.c @@ -0,0 +1,263 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "init.h" + + +static void createSaveFolder(void); +static void loadConfig(void); +void saveConfig(void); +static void initColor(SDL_Color *c, int r, int g, int b); + +void initSDL(void) +{ + int rendererFlags, windowFlags; + + /* do this here, so we don't destroy the save dir stored in app */ + memset(&app, 0, sizeof(App)); + + #if UNIX + createSaveFolder(); + #endif + + rendererFlags = SDL_RENDERER_ACCELERATED; + windowFlags = 0; + + loadConfig(); + + if (app.vSync) + { + rendererFlags |= SDL_RENDERER_PRESENTVSYNC; + } + + if (app.fullscreen) + { + windowFlags |= SDL_WINDOW_FULLSCREEN; + } + + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) + { + printf("Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + SDL_ShowCursor(0); + + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1) + { + printf("Couldn't initialize SDL Mixer\n"); + exit(1); + } + + Mix_AllocateChannels(64); + + Mix_Volume(-1, app.soundVolume * MIX_MAX_VOLUME / 10); + Mix_VolumeMusic(app.musicVolume * MIX_MAX_VOLUME / 10); + + app.window = SDL_CreateWindow("TBFTSS", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, app.winWidth, app.winHeight, windowFlags); + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + + app.renderer = SDL_CreateRenderer(app.window, -1, rendererFlags); + + IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG); + + if (TTF_Init() < 0) + { + printf("Couldn't initialize SDL TTF: %s\n", SDL_GetError()); + exit(1); + } + + app.backBuffer = SDL_CreateTexture(app.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); + + app.scaleX = SCREEN_WIDTH; + app.scaleX /= app.winWidth; + app.scaleY = SCREEN_HEIGHT; + app.scaleY /= app.winHeight; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Game scale factor: %.2f,%.2f\n", app.scaleX, app.scaleY); +} + +#if UNIX +static void createSaveFolder(void) +{ + char *userHome; + char dir[MAX_FILENAME_LENGTH]; + + userHome = getenv("HOME"); + + if (!userHome) + { + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "Unable to determine user save folder. Will save to current dir."); + return; + } + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "User home = %s", userHome); + + sprintf(dir, "%s/.local/share/tbftss", userHome); + if (mkdir(dir, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) != 0 && errno != EEXIST) + { + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "Failed to create save dir '%s'. Will save to current dir.", dir); + return; + } + + STRNCPY(app.saveDir, dir, MAX_FILENAME_LENGTH); +} +#endif + +void initGameSystem(void) +{ + initColor(&colors.red, 255, 0, 0); + initColor(&colors.orange, 255, 128, 0); + initColor(&colors.yellow, 255, 255, 0); + initColor(&colors.green, 0, 255, 0); + initColor(&colors.blue, 0, 0, 255); + initColor(&colors.cyan, 0, 255, 255); + initColor(&colors.purple, 255, 0, 255); + initColor(&colors.white, 255, 255, 255); + initColor(&colors.black, 0, 0, 0); + initColor(&colors.lightGrey, 192, 192, 192); + initColor(&colors.darkGrey, 128, 128, 128); + + initLookups(); + + initFonts(); + + initSounds(); + + initWidgets(); + + initGame(); + + loadFighterDefs(); + + initBulletDefs(); + + initStarSystems(); + + initBattle(); +} + +static void initColor(SDL_Color *c, int r, int g, int b) +{ + memset(c, 0, sizeof(SDL_Color)); + c->r = r; + c->g = g; + c->b = b; + c->a = 255; +} + +static void loadConfig(void) +{ + cJSON *root; + char *text, *configFilename; + + configFilename = getSaveFilePath("config.json"); + + if (fileExists(configFilename)) + { + text = readFile(configFilename); + } + else + { + text = readFile("data/app/config.json"); + + /* so that the player doesn't get confused */ + saveConfig(); + } + + root = cJSON_Parse(text); + + app.winWidth = cJSON_GetObjectItem(root, "winWidth")->valueint; + app.winHeight = cJSON_GetObjectItem(root, "winHeight")->valueint; + app.vSync = cJSON_GetObjectItem(root, "vSync")->valueint; + app.fullscreen = cJSON_GetObjectItem(root, "fullscreen")->valueint; + app.musicVolume = cJSON_GetObjectItem(root, "musicVolume")->valueint; + app.soundVolume = cJSON_GetObjectItem(root, "soundVolume")->valueint; + + cJSON_Delete(root); + free(text); +} + +void saveConfig(void) +{ + char *out, *configFilename; + cJSON *root; + + configFilename = getSaveFilePath("config.json"); + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Saving config ..."); + + root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "winWidth", app.winWidth); + cJSON_AddNumberToObject(root, "winHeight", app.winHeight); + cJSON_AddNumberToObject(root, "vSync", app.vSync); + cJSON_AddNumberToObject(root, "fullscreen", app.fullscreen); + cJSON_AddNumberToObject(root, "musicVolume", app.musicVolume); + cJSON_AddNumberToObject(root, "soundVolume", app.soundVolume); + + out = cJSON_Print(root); + + if (!writeFile(configFilename, out)) + { + printf("Failed to save config\n"); + } + + cJSON_Delete(root); + free(out); +} + +void cleanup(void) +{ + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Cleaning up ..."); + + SDL_DestroyRenderer(app.renderer); + SDL_DestroyWindow(app.window); + + destroyLookups(); + + destroyTextures(); + + expireTexts(); + + destroyFonts(); + + destroySounds(); + + destroyGame(); + + destroyFighterDefs(); + + destroyBulletDefs(); + + destroyStarSystems(); + + destroyBattle(); + + destroyGalacticMap(); + + destroyWidgets(); + + TTF_Quit(); + + SDL_Quit(); + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Done"); +} diff --git a/src/system/init.h b/src/system/init.h new file mode 100644 index 0000000..863e3fc --- /dev/null +++ b/src/system/init.h @@ -0,0 +1,64 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_image.h" +#include "SDL2/SDL_mixer.h" +#include "SDL2/SDL_ttf.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +#if UNIX + #include + #include + #include + #include +#endif + +extern int fileExists(char *filename); +extern char *readFile(char *filename); +extern int writeFile(char *filename, char *data); +extern char *getSaveFilePath(char *filename); +extern void initSounds(void); +extern void loadFighterDefs(void); +extern void initFonts(void); +extern void initBulletDefs(void); +extern void initLookups(void); +extern void initBattle(void); +extern void initGame(void); +extern void initStarSystems(void); +extern void initWidgets(void); +extern void destroyLookups(void); +extern void destroyFonts(void); +extern void destroySounds(void); +extern void destroyGame(void); +extern void destroyFighterDefs(void); +extern void destroyBulletDefs(void); +extern void destroyStarSystems(void); +extern void destroyBattle(void); +extern void destroyTextures(void); +extern void destroyGalacticMap(void); +extern void destroyWidgets(void); +extern void expireTexts(void); + +extern App app; +extern Colors colors; diff --git a/src/system/io.c b/src/system/io.c new file mode 100644 index 0000000..f93c7d9 --- /dev/null +++ b/src/system/io.c @@ -0,0 +1,75 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "io.h" + +int fileExists(char *filename) +{ + struct stat buffer; + + return (stat(filename, &buffer) == 0); +} + +char *readFile(char *filename) +{ + char *buffer = 0; + long length; + FILE *file = fopen(filename, "rb"); + + if (file) + { + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + buffer = malloc(length); + memset(buffer, 0, length); + fread(buffer, 1, length, file); + + fclose(file); + } + + return buffer; +} + +int writeFile(char *filename, char *data) +{ + FILE *file = fopen(filename, "wb"); + + if (file) + { + fprintf(file, "%s\n", data); + fclose(file); + return 1; + } + + return 0; +} + +char *getSaveFilePath(char *filename) +{ + static char path[MAX_FILENAME_LENGTH]; + + sprintf(path, "%s/%s", app.saveDir, filename); + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "getSaveFilePath = '%s'", path); + + return path; +} diff --git a/src/system/io.h b/src/system/io.h new file mode 100644 index 0000000..99aaeb9 --- /dev/null +++ b/src/system/io.h @@ -0,0 +1,27 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "sys/stat.h" + +#include "../defs.h" +#include "../structs.h" + +extern App app; diff --git a/src/system/load.c b/src/system/load.c new file mode 100644 index 0000000..5942235 --- /dev/null +++ b/src/system/load.c @@ -0,0 +1,109 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "load.h" + +static void loadStats(cJSON *stats); +static void loadStarSystems(cJSON *starSystemsJSON); +static void loadMissions(StarSystem *starSystem, cJSON *missionsCJSON); +static void loadChallenges(Mission *mission, cJSON *challengesCJSON); + +void loadGame(void) +{ + cJSON *root, *game; + char *text; + + text = readFile(getSaveFilePath("game.save")); + root = cJSON_Parse(text); + + game = cJSON_GetObjectItem(root, "game"); + + loadStarSystems(cJSON_GetObjectItem(game, "starSystems")); + + loadStats(cJSON_GetObjectItem(game, "stats")); + + cJSON_Delete(root); + free(text); +} + +static void loadStarSystems(cJSON *starSystemsJSON) +{ + StarSystem *starSystem; + cJSON *starSystemJSON; + + for (starSystemJSON = starSystemsJSON->child ; starSystemJSON != NULL ; starSystemJSON = starSystemJSON->next) + { + starSystem = getStarSystem(cJSON_GetObjectItem(starSystemJSON, "name")->valuestring); + + loadMissions(starSystem, cJSON_GetObjectItem(starSystemJSON, "missions")); + } +} + +static void loadMissions(StarSystem *starSystem, cJSON *missionsCJSON) +{ + Mission *mission; + cJSON *missionCJSON; + + for (missionCJSON = missionsCJSON->child ; missionCJSON != NULL ; missionCJSON = missionCJSON->next) + { + mission = getMission(starSystem, cJSON_GetObjectItem(missionCJSON, "filename")->valuestring); + + mission->completed = cJSON_GetObjectItem(missionCJSON, "completed")->valueint; + + if (cJSON_GetObjectItem(missionCJSON, "challenges")) + { + loadChallenges(mission, cJSON_GetObjectItem(missionCJSON, "challenges")); + } + } +} + +static void loadChallenges(Mission *mission, cJSON *challengesCJSON) +{ + Challenge *challenge; + cJSON *challengeCJSON; + + for (challengeCJSON = challengesCJSON->child ; challengeCJSON != NULL ; challengeCJSON = challengeCJSON->next) + { + challenge = getChallenge(mission, lookup(cJSON_GetObjectItem(challengeCJSON, "type")->valuestring)); + + if (!challenge) + { + printf("Couldn't find challenge to update\n"); + continue; + } + + challenge->passed = cJSON_GetObjectItem(challengeCJSON, "passed")->valueint; + } +} + +static void loadStats(cJSON *stats) +{ + game.stats.missionsStarted = cJSON_GetObjectItem(stats, "missionsStarted")->valueint; + game.stats.missionsCompleted = cJSON_GetObjectItem(stats, "missionsCompleted")->valueint; + game.stats.shotsFired = cJSON_GetObjectItem(stats, "shotsFired")->valueint; + game.stats.shotsHit = cJSON_GetObjectItem(stats, "shotsHit")->valueint; + game.stats.missilesFired = cJSON_GetObjectItem(stats, "missilesFired")->valueint; + game.stats.missilesHit = cJSON_GetObjectItem(stats, "missilesHit")->valueint; + game.stats.enemiesKilled = cJSON_GetObjectItem(stats, "enemiesKilled")->valueint; + game.stats.alliesKilled = cJSON_GetObjectItem(stats, "alliesKilled")->valueint; + game.stats.playerKilled = cJSON_GetObjectItem(stats, "playerKilled")->valueint; + game.stats.playerKills = cJSON_GetObjectItem(stats, "playerKills")->valueint; + game.stats.time = cJSON_GetObjectItem(stats, "time")->valueint; +} diff --git a/src/system/load.h b/src/system/load.h new file mode 100644 index 0000000..0fcca55 --- /dev/null +++ b/src/system/load.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern char *readFile(char *filename); +extern StarSystem *getStarSystem(char *name); +extern Mission *getMission(StarSystem *starSystem, char *filename); +extern Challenge *getChallenge(Mission *mission, int type); +extern int lookup(char *lookup); +extern char *getSaveFilePath(char *filename); + +extern Game game; diff --git a/src/system/lookup.c b/src/system/lookup.c new file mode 100644 index 0000000..87bb20e --- /dev/null +++ b/src/system/lookup.c @@ -0,0 +1,175 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "lookup.h" + +static Lookup head; +static Lookup *tail; + +static void addLookup(char *name, long value); + +void initLookups(void) +{ + memset(&head, 0, sizeof(Lookup)); + tail = &head; + + addLookup("WT_BUTTON", WT_BUTTON); + addLookup("WT_SELECT", WT_SELECT); + + addLookup("SIDE_ALLIES", SIDE_ALLIES); + addLookup("SIDE_PIRATE", SIDE_PIRATE); + addLookup("SIDE_PANDORAN", SIDE_PANDORAN); + addLookup("SIDE_CSN", SIDE_CSN); + addLookup("SIDE_UNF", SIDE_UNF); + addLookup("SIDE_INF", SIDE_INF); + + addLookup("SND_PARTICLE", SND_PARTICLE); + addLookup("SND_PLASMA", SND_PLASMA); + addLookup("SND_MAG", SND_MAG); + + addLookup("BT_PARTICLE", BT_PARTICLE); + addLookup("BT_PLASMA", BT_PLASMA); + addLookup("BT_LASER", BT_LASER); + addLookup("BT_MAG", BT_MAG); + addLookup("BT_ROCKET", BT_ROCKET); + addLookup("BT_MISSILE", BT_MISSILE); + + addLookup("BF_NONE", BF_NONE); + addLookup("BF_ENGINE", BF_ENGINE); + + addLookup("MISSILE_ROCKET", MISSILE_ROCKET); + addLookup("MISSILE_MISSILE", MISSILE_MISSILE); + addLookup("MISSILE_FF", MISSILE_FF); + + addLookup("CHALLENGE_ARMOUR", CHALLENGE_ARMOUR); + addLookup("CHALLENGE_TIME", CHALLENGE_TIME); + addLookup("CHALLENGE_ACCURACY", CHALLENGE_ACCURACY); + addLookup("CHALLENGE_NO_LOSSES", CHALLENGE_NO_LOSSES); + addLookup("CHALLENGE_1_LOSS", CHALLENGE_1_LOSS); + addLookup("CHALLENGE_LOSSES", CHALLENGE_LOSSES); + addLookup("CHALLENGE_PLAYER_KILLS", CHALLENGE_PLAYER_KILLS); +} + +static void addLookup(char *name, long value) +{ + Lookup *lookup = malloc(sizeof(Lookup)); + memset(lookup, 0, sizeof(Lookup)); + + STRNCPY(lookup->name, name, MAX_NAME_LENGTH); + lookup->value = value; + + tail->next = lookup; + tail = lookup; +} + +long lookup(char *name) +{ + Lookup *l; + + for (l = head.next ; l != NULL ; l = l->next) + { + if (strcmp(l->name, name) == 0) + { + return l->value; + } + } + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "No such lookup value '%s'", name); + + exit(1); + + return 0; +} + +char *getLookupName(char *prefix, long num) +{ + Lookup *l; + + for (l = head.next ; l != NULL ; l = l->next) + { + if (l->value == num && strncmp(prefix, l->name, strlen(prefix)) == 0) + { + return l->name; + } + } + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "No such lookup value %d, prefix=%s", num, prefix); + + exit(1); + + return ""; +} + +char *getFlagValues(char *prefix, long flags) +{ + static char flagStr[MAX_DESCRIPTION_LENGTH]; + int requirePlus; + Lookup *l; + + memset(flagStr, '\0', MAX_DESCRIPTION_LENGTH); + + requirePlus = 0; + + for (l = head.next ; l != NULL ; l = l->next) + { + if (flags & l->value && strncmp(prefix, l->name, strlen(prefix)) == 0) + { + if (requirePlus) + { + strcat(flagStr, "+"); + } + + strcat(flagStr, l->name); + + requirePlus = 1; + } + } + + return flagStr; +} + +long flagsToLong(char *flags) +{ + char *flag; + long total; + + total = 0; + + flag = strtok(flags, "+"); + while (flag) + { + total += lookup(flag); + flag = strtok(NULL, "+"); + } + + return total; +} + +void destroyLookups(void) +{ + Lookup *l; + + while (head.next) + { + l = head.next; + head.next = l->next; + free(l); + } +} diff --git a/src/system/lookup.h b/src/system/lookup.h new file mode 100644 index 0000000..bc5e47d --- /dev/null +++ b/src/system/lookup.h @@ -0,0 +1,24 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" diff --git a/src/system/options.c b/src/system/options.c new file mode 100644 index 0000000..303df56 --- /dev/null +++ b/src/system/options.c @@ -0,0 +1,126 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "options.h" + +static void changeWindowSize(char *value); +static void changeSoundVolume(char *value); +static void changeMusicVolume(char *value); +static void changeVSync(char *value); +static void changeFullscreen(char *value); +static void ok(void); + +static void (*returnFromOptions)(void); + +void initOptions(void (*rtn)(void)) +{ + char optionStr[MAX_NAME_LENGTH]; + + selectWidget("windowSize", "options"); + + getWidget("windowSize", "options")->onChange = changeWindowSize; + getWidget("soundVolume", "options")->onChange = changeSoundVolume; + getWidget("musicVolume", "options")->onChange = changeMusicVolume; + getWidget("vSync", "options")->onChange = changeVSync; + getWidget("fullscreen", "options")->onChange = changeFullscreen; + getWidget("ok", "options")->action = ok; + + sprintf(optionStr, "%d x %d", app.winWidth, app.winHeight); + setWidgetOption("windowSize", "options", optionStr); + + sprintf(optionStr, "%d", app.soundVolume); + setWidgetOption("soundVolume", "options", optionStr); + + sprintf(optionStr, "%d", app.musicVolume); + setWidgetOption("musicVolume", "options", optionStr); + + setWidgetOption("vSync", "options", app.vSync ? "On" : "Off"); + + setWidgetOption("fullscreen", "options", app.fullscreen ? "On" : "Off"); + + returnFromOptions = rtn; +} + +void drawOptions(void) +{ + SDL_Rect r; + + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128); + SDL_RenderFillRect(app.renderer, NULL); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + + r.w = 500; + r.h = 600; + r.x = (SCREEN_WIDTH / 2) - r.w / 2; + r.y = (SCREEN_HEIGHT / 2) - r.h / 2; + + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 0); + SDL_RenderFillRect(app.renderer, &r); + SDL_SetRenderDrawColor(app.renderer, 200, 200, 200, 255); + SDL_RenderDrawRect(app.renderer, &r); + + drawText(SCREEN_WIDTH / 2, 70, 28, TA_CENTER, colors.white, "Options"); + + SDL_SetRenderDrawColor(app.renderer, 128, 128, 128, 255); + SDL_RenderDrawLine(app.renderer, r.x, 120, r.x + r.w, 120); + + drawWidgets("options"); + + limitTextWidth(r.w - 100); + drawText(SCREEN_WIDTH / 2, r.y + r.h - 135, 16, TA_CENTER, colors.yellow, "Note: you must restart the game for window size and fullscreen options to take effect."); + limitTextWidth(0); +} + +static void changeWindowSize(char *value) +{ + sscanf(value, "%d x %d", &app.winWidth, &app.winHeight); +} + +static void changeSoundVolume(char *value) +{ + app.soundVolume = atoi(value); + + Mix_Volume(-1, app.soundVolume * MIX_MAX_VOLUME / 10); +} + +static void changeMusicVolume(char *value) +{ + app.musicVolume = atoi(value); + + Mix_VolumeMusic(app.musicVolume * MIX_MAX_VOLUME / 10); +} + +static void changeVSync(char *value) +{ + app.vSync = strcmp(value, "On") == 0; +} + +static void changeFullscreen(char *value) +{ + app.fullscreen = strcmp(value, "On") == 0; +} + +static void ok(void) +{ + saveConfig(); + + returnFromOptions(); +} diff --git a/src/system/options.h b/src/system/options.h new file mode 100644 index 0000000..df93865 --- /dev/null +++ b/src/system/options.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_mixer.h" + +#include "../defs.h" +#include "../structs.h" + +extern void selectWidget(const char *name, const char *group); +extern Widget *getWidget(const char *name, const char *group); +extern void drawWidgets(char *groupName); +extern void setWidgetOption(const char *name, const char *group, const char *value); +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern void saveConfig(void); +extern void limitTextWidth(int width); + +extern App app; +extern Colors colors; diff --git a/src/system/save.c b/src/system/save.c new file mode 100644 index 0000000..4c2126d --- /dev/null +++ b/src/system/save.c @@ -0,0 +1,134 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "save.h" + +static void saveStarSystems(cJSON *gameJSON); +static cJSON *getMissionsJSON(StarSystem *starSystem); +static cJSON *getChallengesJSON(Mission *mission); +static void saveStats(cJSON *gameJSON); + +void saveGame(void) +{ + char *out; + cJSON *root, *gameJSON; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Saving Game ..."); + + root = cJSON_CreateObject(); + gameJSON = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "game", gameJSON); + + saveStarSystems(gameJSON); + + saveStats(gameJSON); + + out = cJSON_Print(root); + + writeFile(getSaveFilePath("game.save"), out); + + cJSON_Delete(root); + free(out); +} + +static void saveStarSystems(cJSON *gameJSON) +{ + cJSON *starSystemJSON, *starSystemsJSON; + StarSystem *starSystem; + + starSystemsJSON = cJSON_CreateArray(); + + for (starSystem = game.starSystemHead.next ; starSystem != NULL ; starSystem = starSystem->next) + { + starSystemJSON = cJSON_CreateObject(); + + cJSON_AddStringToObject(starSystemJSON, "name", starSystem->name); + cJSON_AddItemToObject(starSystemJSON, "missions", getMissionsJSON(starSystem)); + + cJSON_AddItemToArray(starSystemsJSON, starSystemJSON); + } + + cJSON_AddItemToObject(gameJSON, "starSystems", starSystemsJSON); +} + +static cJSON *getMissionsJSON(StarSystem *starSystem) +{ + cJSON *missionJSON, *missionsJSON; + Mission *mission; + + missionsJSON = cJSON_CreateArray(); + + for (mission = starSystem->missionHead.next ; mission != NULL ; mission = mission->next) + { + missionJSON = cJSON_CreateObject(); + + cJSON_AddStringToObject(missionJSON, "filename", mission->filename); + cJSON_AddNumberToObject(missionJSON, "completed", mission->completed); + + if (mission->challengeHead.next) + { + cJSON_AddItemToObject(missionJSON, "challenges", getChallengesJSON(mission)); + } + + cJSON_AddItemToArray(missionsJSON, missionJSON); + } + + return missionsJSON; +} + +static cJSON *getChallengesJSON(Mission *mission) +{ + cJSON *challengesJSON, *challengeJSON; + Challenge *challenge; + + challengesJSON = cJSON_CreateArray(); + + for (challenge = mission->challengeHead.next ; challenge != NULL ; challenge = challenge->next) + { + challengeJSON = cJSON_CreateObject(); + + cJSON_AddStringToObject(challengeJSON, "type", getLookupName("CHALLENGE_", challenge->type)); + cJSON_AddNumberToObject(challengeJSON, "targetValue", challenge->targetValue); + cJSON_AddNumberToObject(challengeJSON, "passed", challenge->passed); + + cJSON_AddItemToArray(challengesJSON, challengeJSON); + } + + return challengesJSON; +} + +static void saveStats(cJSON *gameJSON) +{ + cJSON *stats = cJSON_CreateObject(); + + cJSON_AddNumberToObject(stats, "missionsStarted", game.stats.missionsStarted); + cJSON_AddNumberToObject(stats, "missionsCompleted", game.stats.missionsCompleted); + cJSON_AddNumberToObject(stats, "shotsFired", game.stats.shotsFired); + cJSON_AddNumberToObject(stats, "shotsHit", game.stats.shotsHit); + cJSON_AddNumberToObject(stats, "missilesFired", game.stats.missilesFired); + cJSON_AddNumberToObject(stats, "missilesHit", game.stats.missilesHit); + cJSON_AddNumberToObject(stats, "enemiesKilled", game.stats.enemiesKilled); + cJSON_AddNumberToObject(stats, "alliesKilled", game.stats.alliesKilled); + cJSON_AddNumberToObject(stats, "playerKilled", game.stats.playerKilled); + cJSON_AddNumberToObject(stats, "playerKills", game.stats.playerKills); + cJSON_AddNumberToObject(stats, "time", game.stats.time); + + cJSON_AddItemToObject(gameJSON, "stats", stats); +} diff --git a/src/system/save.h b/src/system/save.h new file mode 100644 index 0000000..ea9c63d --- /dev/null +++ b/src/system/save.h @@ -0,0 +1,31 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern int writeFile(char *filename, char *data); +extern char *getLookupName(char *prefix, long num); +extern char *getSaveFilePath(char *filename); + +extern Game game; diff --git a/src/system/sound.c b/src/system/sound.c new file mode 100644 index 0000000..8bc84c2 --- /dev/null +++ b/src/system/sound.c @@ -0,0 +1,124 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "sound.h" + +static void loadSounds(void); + +static Mix_Chunk *sounds[SND_MAX]; +static Mix_Music *music; + +static int lastPlayerX; +static int lastPlayerY; + +void initSounds(void) +{ + memset(sounds, 0, sizeof(Mix_Chunk*) * SND_MAX); + + music = NULL; + + loadSounds(); +} + +void playMusic(char *filename) +{ + if (music != NULL) + { + Mix_HaltMusic(); + Mix_FreeMusic(music); + music = NULL; + } + + music = Mix_LoadMUS(filename); + + Mix_PlayMusic(music, -1); +} + +void stopMusic(void) +{ + Mix_HaltMusic(); +} + +void playSound(int id) +{ + Mix_PlayChannel(-1, sounds[id], 0); +} + +void playBattleSound(int id, int x, int y) +{ + float distance; + int channel; + float vol; + + if (player != NULL) + { + lastPlayerX = player->x; + lastPlayerY = player->y; + } + + distance = getDistance(lastPlayerX, lastPlayerY, x, y); + + if (distance <= MAX_BATTLE_SOUND_DISTANCE) + { + channel = Mix_PlayChannel(-1, sounds[id], 0); + if (channel != -1) + { + vol = 255; + vol /= MAX_BATTLE_SOUND_DISTANCE; + vol *= distance; + + Mix_SetDistance(channel, vol); + } + } +} + +static void loadSounds(void) +{ + sounds[SND_ARMOUR_HIT] = Mix_LoadWAV("sound/275151__bird-man__gun-shot.ogg"); + sounds[SND_PLASMA] = Mix_LoadWAV("sound/268344__julien-matthey__jm-noiz-laser-01.ogg"); + sounds[SND_PARTICLE] = Mix_LoadWAV("sound/77087__supraliminal__laser-short.ogg"); + sounds[SND_EXPLOSION_1] = Mix_LoadWAV("sound/162265__qubodup__explosive.ogg"); + sounds[SND_EXPLOSION_2] = Mix_LoadWAV("sound/207322__animationisaac__short-explosion.ogg"); + sounds[SND_EXPLOSION_3] = Mix_LoadWAV("sound/254071__tb0y298__firework-explosion.ogg"); + sounds[SND_EXPLOSION_4] = Mix_LoadWAV("sound/47252__nthompson__bad-explosion.ogg"); + + sounds[SND_GUI_CLICK] = Mix_LoadWAV("sound/257786__xtrgamr__mouse-click.ogg"); + sounds[SND_GUI_SELECT] = Mix_LoadWAV("sound/321104__nsstudios__blip2.ogg"); + sounds[SND_GUI_CLOSE] = Mix_LoadWAV("sound/178064__jorickhoofd__slam-door-shut.ogg"); + sounds[SND_GUI_DENIED] = Mix_LoadWAV("sound/249300__suntemple__access-denied.ogg"); +} + +void destroySounds(void) +{ + int i; + + for (i = 0 ; i < SND_MAX ; i++) + { + if (sounds[i]) + { + Mix_FreeChunk(sounds[i]); + } + } + + if (music != NULL) + { + Mix_FreeMusic(music); + } +} diff --git a/src/system/sound.h b/src/system/sound.h new file mode 100644 index 0000000..eea849a --- /dev/null +++ b/src/system/sound.h @@ -0,0 +1,32 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_mixer.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +#define MAX_BATTLE_SOUND_DISTANCE 3000 + +extern int getDistance(int x1, int y1, int x2, int y2); + +extern Fighter *player; diff --git a/src/system/textures.c b/src/system/textures.c new file mode 100644 index 0000000..ebad338 --- /dev/null +++ b/src/system/textures.c @@ -0,0 +1,109 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "textures.h" + +static Texture textures[NUM_TEXTURE_BUCKETS]; + +static void addTextureToCache(char *name, SDL_Texture *texture); +SDL_Texture *getTexture(char *name); + +void initTextures(void) +{ + memset(&textures, 0, sizeof(Texture) * NUM_TEXTURE_BUCKETS); +} + +static void addTextureToCache(char *name, SDL_Texture *texture) +{ + Texture *t, *new; + int i; + + i = strlen(name) % NUM_TEXTURE_BUCKETS; + + t = &textures[i]; + + /* horrible bit to look for the tail */ + while (t->next) + { + t = t->next; + } + + new = malloc(sizeof(Texture)); + memset(new, 0, sizeof(Texture)); + + STRNCPY(new->name, name, MAX_NAME_LENGTH); + new->texture = texture; + + t->next = new; +} + +static SDL_Texture *loadTexture(char *filename) +{ + SDL_Texture *texture; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", filename); + + texture = IMG_LoadTexture(app.renderer, filename); + + addTextureToCache(filename, texture); + + return texture; +} + + +SDL_Texture *getTexture(char *filename) +{ + Texture *t; + int i; + + i = strlen(filename) % NUM_TEXTURE_BUCKETS; + + /* check if the texture is already loaded */ + for (t = textures[i].next ; t != NULL ; t = t->next) + { + if (strcmp(t->name, filename) == 0) + { + return t->texture; + } + } + + return loadTexture(filename); +} + +void destroyTextures(void) +{ + Texture *t, *next; + int i; + + for (i = 0 ; i < NUM_TEXTURE_BUCKETS ; i++) + { + t = textures[i].next; + + while (t) + { + next = t->next; + SDL_DestroyTexture(t->texture); + free(t); + t = next; + } + + textures[i].next = NULL; + } +} diff --git a/src/system/textures.h b/src/system/textures.h new file mode 100644 index 0000000..4a072bc --- /dev/null +++ b/src/system/textures.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL_image.h" + +#include "../defs.h" +#include "../structs.h" + +extern App app; diff --git a/src/system/transition.c b/src/system/transition.c new file mode 100644 index 0000000..7904dc9 --- /dev/null +++ b/src/system/transition.c @@ -0,0 +1,44 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "transition.h" + +static long transitionStartTime; + +void startSectionTransition(void) +{ + transitionStartTime = SDL_GetTicks(); + + prepareScene(); + + presentScene(); + + expireTexts(); +} + +void endSectionTransition(void) +{ + long elasped = 250 - (SDL_GetTicks() - transitionStartTime); + + if (elasped > 0) + { + SDL_Delay(elasped); + } +} diff --git a/src/system/transition.h b/src/system/transition.h new file mode 100644 index 0000000..6ef2cbd --- /dev/null +++ b/src/system/transition.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" + +extern void prepareScene(void); +extern void presentScene(void); +extern void expireTexts(void); diff --git a/src/system/util.c b/src/system/util.c new file mode 100644 index 0000000..8282828 --- /dev/null +++ b/src/system/util.c @@ -0,0 +1,74 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "util.h" + +int mod(int n, int x) +{ + return ((n % x) + x) % x; +} + +int rrnd(int low, int high) +{ + return low + rand() % ((high - low) + 1); +} + +int getPercent(float current, float total) +{ + return (current / total) * 100; +} + +int collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) +{ + return (MAX(x1, x2) < MIN(x1 + w1, x2 + w2)) && (MAX(y1, y2) < MIN(y1 + h1, y2 + h2)); +} + +float getAngle(int x1, int y1, int x2, int y2) +{ + float angle = -90 + atan2(y1 - y2, x1 - x2) * (180 / PI); + return angle >= 0 ? angle : 360 + angle; +} + +int getDistance(int x1, int y1, int x2, int y2) +{ + int x, y; + + x = x2 - x1; + y = y2 - y1; + + return sqrt(x * x + y *y); +} + +void getSlope(int x1, int y1, int x2, int y2, float *dx, float *dy) +{ + int steps = MAX(abs(x1 - x2), abs(y1 - y2)); + + if (steps == 0) + { + *dx = *dy = 0; + return; + } + + *dx = (x1 - x2); + *dx /= steps; + + *dy = (y1 - y2); + *dy /= steps; +} diff --git a/src/system/util.h b/src/system/util.h new file mode 100644 index 0000000..3ed4016 --- /dev/null +++ b/src/system/util.h @@ -0,0 +1,23 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" diff --git a/src/system/widgets.c b/src/system/widgets.c new file mode 100644 index 0000000..41d03d3 --- /dev/null +++ b/src/system/widgets.c @@ -0,0 +1,392 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "widgets.h" + +static void loadWidgets(char *filename); +static void loadWidgetSet(char *filename); +static void handleKeyboard(void); +static void createOptions(Widget *w, char *options); + +static Widget head; +static Widget *tail; +static Widget *selectedWidget; +static SDL_Texture *optionsLeft; +static SDL_Texture *optionsRight; +static int drawingWidgets; + +void initWidgets(void) +{ + memset(&head, 0, sizeof(Widget)); + + tail = &head; + + selectedWidget = NULL; + + loadWidgets("data/widgets/list.json"); + + drawingWidgets = 0; + + optionsLeft = getTexture("gfx/widgets/optionsLeft.png"); + optionsRight = getTexture("gfx/widgets/optionsRight.png"); +} + +void doWidgets(void) +{ + if (drawingWidgets) + { + handleKeyboard(); + } + + drawingWidgets = 0; +} + +Widget *getWidget(const char *name, const char *group) +{ + Widget *w; + + for (w = head.next; w != NULL ; w = w->next) + { + if (strcmp(w->name, name) == 0 && strcmp(w->group, group) == 0) + { + return w; + } + } + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "No such widget ('%s', '%s')", name, group); + + return NULL; +} + +void selectWidget(const char *name, const char *group) +{ + selectedWidget = getWidget(name, group); +} + +void drawWidgets(const char *group) +{ + Widget *w; + + drawingWidgets = 1; + + for (w = head.next; w != NULL ; w = w->next) + { + if (w->visible && strcmp(w->group, group) == 0) + { + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderFillRect(app.renderer, &w->rect); + + if (w == selectedWidget) + { + SDL_SetRenderDrawColor(app.renderer, 64, 128, 200, SDL_ALPHA_OPAQUE); + SDL_RenderFillRect(app.renderer, &w->rect); + SDL_SetRenderDrawColor(app.renderer, 128, 192, 255, SDL_ALPHA_OPAQUE); + SDL_RenderDrawRect(app.renderer, &w->rect); + } + else + { + SDL_SetRenderDrawColor(app.renderer, 64, 64, 64, SDL_ALPHA_OPAQUE); + SDL_RenderDrawRect(app.renderer, &w->rect); + } + + switch (w->type) + { + case WT_BUTTON: + SDL_RenderDrawRect(app.renderer, &w->rect); + drawText(w->rect.x + (w->rect.w / 2), w->rect.y + 2, 20, TA_CENTER, colors.white, w->text); + break; + + case WT_SELECT: + drawText(w->rect.x + 10, w->rect.y + 2, 20, TA_LEFT, colors.white, w->text); + drawText(w->rect.x + w->rect.w - 10, w->rect.y + 2, 20, TA_RIGHT, colors.white, w->options[w->currentOption]); + + if (w->currentOption != 0) + { + blit(optionsLeft, w->rect.x - 24, w->rect.y + 16, 1); + } + + if (w->currentOption != w->numOptions - 1) + { + blit(optionsRight, w->rect.x + w->rect.w + 24, w->rect.y + 16, 1); + } + + break; + } + + if (!w->enabled) + { + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 192); + SDL_RenderFillRect(app.renderer, &w->rect); + SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); + } + } + } +} + +void drawConfirmMessage(char *message) +{ + drawWidgets("okCancel"); +} + +static void gotoWidget(int dx, int dy) +{ + Widget *w, *closest; + int distance; + int curDistance = -1; + + closest = selectedWidget; + + for (w = head.next; w != NULL ; w = w->next) + { + if (w == selectedWidget || + !w->enabled || + !w->visible || + strcmp(w->group, selectedWidget->group) != 0 || + (dx == -1 && w->rect.x > selectedWidget->rect.x) || + (dx == 1 && w->rect.x < selectedWidget->rect.x) || + (dx != 0 && w->rect.x == selectedWidget->rect.x) || + (dy == -1 && w->rect.y > selectedWidget->rect.y) || + (dy == 1 && w->rect.y < selectedWidget->rect.y) || + (dy != 0 && w->rect.y == selectedWidget->rect.y) + ) + { + continue; + } + + distance = getDistance(w->rect.x, w->rect.y, selectedWidget->rect.x, selectedWidget->rect.y); + + if (curDistance == -1 || distance < curDistance) + { + curDistance = distance; + closest = w; + } + } + + if (selectedWidget != closest) + { + playSound(SND_GUI_CLICK); + + selectedWidget = closest; + } +} + +static void changeSelectValue(int dir) +{ + int oldOption = selectedWidget->currentOption; + + selectedWidget->currentOption += dir; + + selectedWidget->currentOption = MIN(MAX(0, selectedWidget->currentOption), selectedWidget->numOptions - 1); + + selectedWidget->onChange(selectedWidget->options[selectedWidget->currentOption]); + + if (oldOption != selectedWidget->currentOption) + { + playSound(SND_GUI_CLICK); + } +} + +void setWidgetOption(const char *name, const char *group, const char *value) +{ + int i; + Widget *w = getWidget(name, group); + + if (w) + { + for (i = 0 ; i < w->numOptions ; i++) + { + if (strcmp(w->options[i], value) == 0) + { + w->currentOption = i; + return; + } + } + } +} + +static void handleKeyboard(void) +{ + if (app.keyboard[SDL_SCANCODE_UP]) + { + gotoWidget(0, -1); + } + + if (app.keyboard[SDL_SCANCODE_DOWN]) + { + gotoWidget(0, 1); + } + + if (app.keyboard[SDL_SCANCODE_LEFT]) + { + if (selectedWidget->type != WT_SELECT) + { + gotoWidget(-1, 0); + } + else + { + changeSelectValue(-1); + } + } + + if (app.keyboard[SDL_SCANCODE_RIGHT]) + { + if (selectedWidget->type != WT_SELECT) + { + gotoWidget(1, 0); + } + else + { + changeSelectValue(1); + } + } + + if (app.keyboard[SDL_SCANCODE_RETURN] && selectedWidget->action) + { + playSound(SND_GUI_SELECT); + + selectedWidget->action(); + } + + memset(app.keyboard, 0, sizeof(int) * MAX_KEYBOARD_KEYS); +} + +static void loadWidgets(char *filename) +{ + cJSON *root, *node; + char *text; + + text = readFile(filename); + root = cJSON_Parse(text); + + for (node = root->child ; node != NULL ; node = node->next) + { + loadWidgetSet(node->valuestring); + } + + cJSON_Delete(root); + free(text); +} + +static void loadWidgetSet(char *filename) +{ + cJSON *root, *node; + char *text; + Widget *w; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", filename); + + text = readFile(filename); + root = cJSON_Parse(text); + + for (node = root->child ; node != NULL ; node = node->next) + { + w = malloc(sizeof(Widget)); + memset(w, 0, sizeof(Widget)); + + w->type = lookup(cJSON_GetObjectItem(node, "type")->valuestring); + STRNCPY(w->name, cJSON_GetObjectItem(node, "name")->valuestring, MAX_NAME_LENGTH); + STRNCPY(w->group, cJSON_GetObjectItem(node, "group")->valuestring, MAX_NAME_LENGTH); + w->rect.x = cJSON_GetObjectItem(node, "x")->valueint; + w->rect.y = cJSON_GetObjectItem(node, "y")->valueint; + w->rect.w = cJSON_GetObjectItem(node, "w")->valueint; + w->rect.h = cJSON_GetObjectItem(node, "h")->valueint; + w->enabled = 1; + w->visible = 1; + + if (w->rect.x == -1) + { + w->rect.x = SCREEN_WIDTH / 2; + } + + switch (w->type) + { + case WT_BUTTON: + STRNCPY(w->text, cJSON_GetObjectItem(node, "text")->valuestring, MAX_NAME_LENGTH); + w->rect.x -= w->rect.w / 2; + w->rect.y -= (w->rect.h / 2) + 8; + break; + + case WT_SELECT: + STRNCPY(w->text, cJSON_GetObjectItem(node, "text")->valuestring, MAX_NAME_LENGTH); + w->rect.x -= w->rect.w / 2; + w->rect.y -= (w->rect.h / 2) + 8; + createOptions(w, cJSON_GetObjectItem(node, "options")->valuestring); + break; + } + + tail->next = w; + tail = w; + } + + cJSON_Delete(root); + free(text); +} + +static void createOptions(Widget *w, char *options) +{ + int i; + char *option; + + w->numOptions = 1; + + for (i = 0 ; i < strlen(options) ; i++) + { + if (options[i] == ';') + { + w->numOptions++; + } + } + + w->options = malloc(w->numOptions * sizeof(char*)); + + i = 0; + option = strtok(options, ";"); + while (option) + { + w->options[i] = malloc(strlen(option) + 1); + strcpy(w->options[i], option); + + option = strtok(NULL, ";"); + + i++; + } +} + +void destroyWidgets(void) +{ + int i; + Widget *w = head.next; + Widget *next; + + while (w) + { + for (i = 0 ; i < w->numOptions ; i++) + { + free(w->options[i]); + } + + next = w->next; + free(w); + w = next; + } + + head.next = NULL; +} diff --git a/src/system/widgets.h b/src/system/widgets.h new file mode 100644 index 0000000..bc9ca06 --- /dev/null +++ b/src/system/widgets.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2015 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "SDL2/SDL.h" + +#include "../defs.h" +#include "../structs.h" +#include "../json/cJSON.h" + +extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); +extern char *readFile(char *filename); +extern long lookup(char *name); +extern int getDistance(int x1, int y1, int x2, int y2); +extern int mod(int n, int x); +extern void blit(SDL_Texture *texture, int x, int y, int centered); +extern SDL_Texture *getTexture(char *filename); +extern void playSound(int id); + +extern App app; +extern Colors colors;