diff options
-rw-r--r-- | data/config.ini | 2 | ||||
-rw-r--r-- | data/customlevels.txt | 41 | ||||
-rw-r--r-- | data/levels.ini | 126 | ||||
-rw-r--r-- | src/Game.h | 2 | ||||
-rw-r--r-- | src/GameInitDispose.cpp | 243 | ||||
-rw-r--r-- | src/config.h | 15 | ||||
-rw-r--r-- | src/config.zig | 138 | ||||
-rw-r--r-- | src/main.zig | 12 |
8 files changed, 287 insertions, 292 deletions
diff --git a/data/config.ini b/data/config.ini index fdd3793..7a9e150 100644 --- a/data/config.ini +++ b/data/config.ini @@ -12,5 +12,5 @@ music = true mouse sensitivity = 1.0 [misc] -custom levels = false +custom levels = 0 debug = false diff --git a/data/customlevels.txt b/data/customlevels.txt deleted file mode 100644 index 4aa9b93..0000000 --- a/data/customlevels.txt +++ /dev/null @@ -1,41 +0,0 @@ -Number of missions: -2 -Level type: (assassin=0, zombie=1) -0 -Environment: (sunny=0, foggy=1, snowy=2, rainy=3, red=4, night=5) -3 -How many different guns can assassins have: -2 -List them: (0=unarmed, 1=sniper rifle, 2=assaultrifle, 3=magnum, 4=handgun, 5=grenade, 6=knife, 7=shotgun -1 -3 -One out of how many civilians is an assassin: -4 -Starting gun: (see above) -6 -How many magazines/grenades: -5 -Time requirement: -30 -Difficulty:(0 to 2, 1=normal) -.6 - -Level type: (assassin=0, zombie=1) -1 -Environment: (sunny=0, foggy=1, snowy=2, rainy=3, red=4, night=5) -4 -How many different guns can assassins have: -2 -List them: (0=unarmed, 1=sniper rifle, 2=assaultrifle, 3=magnum, 4=handgun, 5=grenade, 6=knife, 7=shotgun -1 -3 -One out of how many civilians is an assassin: -4 -Starting gun: (see above) -3 -How many magazines/grenades: -10 -Time requirement: -25 -Difficulty:(0 to 2, 1=normal) -.8 diff --git a/data/levels.ini b/data/levels.ini new file mode 100644 index 0000000..7eb6be1 --- /dev/null +++ b/data/levels.ini @@ -0,0 +1,126 @@ +; Weapons enums: +; 0: none/zombie +; 1: sniper rifle +; 2: assault rifle +; 3: magnum +; 4: handgun +; 5: grenade +; 6: knife +; 7: shotgun + +[level 0] +environment = sunny +evil weapons = 3 4 7 +evil rarity = 6 +guard weapon = 2 +guard reloads = 6 +time = 50 +difficulty = 0.6 + +[level 1] +environment = snowy +evil weapons = 2 6 +evil rarity = 5 +guard weapon = 4 +guard reloads = 3 +time = 40 +difficulty = 0.6 + +[level 2] +environment = foggy +evil weapons = 1 +evil rarity = 5 +guard weapon = 1 +guard reloads = 4 +time = 50 +difficulty = 0.9 + +[level 3] +environment = firey +evil weapons = 0 +evil rarity = 5 +guard weapon = 7 +guard reloads = 5 +time = 35 +difficulty = 0.7 + +[level 4] +environment = snowy +evil weapons = 1 2 +evil rarity = 5 +guard weapon = 5 +guard reloads = 20 +time = 30 +difficulty = 0.5 + +[level 5] +environment = rainy +evil weapons = 2 3 7 +evil rarity = 6 +guard weapon = 6 +guard reloads = 3 +time = 40 +difficulty = 0.8 + +[level 6] +environment = night +evil weapons = 3 4 7 +evil rarity = 5 +guard weapon = 3 +guard reloads = 4 +time = 30 +difficulty = 1.0 + +[level 7] +environment = firey +evil weapons = 0 +evil rarity = 5 +guard weapon = 2 +guard reloads = 5 +time = 30 +difficulty = 1.0 + +[level 8] +environment = rainy +evil weapons = 1 2 3 4 7 +evil rarity = 5 +guard weapon = 0 +guard reloads = 3 +time = 40 +difficulty = 0.8 + +[level 9] +environment = snowy +evil weapons = 1 2 3 4 6 7 +evil rarity = 4 +guard weapon = 3 +guard reloads = 3 +time = 90 +difficulty = 1.0 + +[level 10] +environment = night +evil weapons = 1 +evil rarity = 5 +guard weapon = 1 +guard reloads = 4 +time = 30 +difficulty = 1.3 + +[level 11] +environment = sunny +evil weapons = 1 6 +evil rarity = 4 +guard weapon = 6 +guard reloads = 4 +time = 30 +difficulty = 1.5 + +[level 12] +environment = firey +evil weapons = 0 +evil rarity = 4 +guard weapon = 4 +guard reloads = 10 +time = 60 +difficulty = 1.5 diff --git a/src/Game.h b/src/Game.h index 39fdcbc..f6400d9 100644 --- a/src/Game.h +++ b/src/Game.h @@ -127,7 +127,6 @@ public: int numpeople; float spawndelay; - bool customlevels; bool musictoggle; float psychicpower; int type; @@ -151,6 +150,7 @@ public: int score; int mission; int nummissions; + struct Level* levels; int numpossibleguns; int possiblegun[6]; int evilprobability; diff --git a/src/GameInitDispose.cpp b/src/GameInitDispose.cpp index bd8a05f..9700e25 100644 --- a/src/GameInitDispose.cpp +++ b/src/GameInitDispose.cpp @@ -27,7 +27,6 @@ #include <AL/alc.h> #include <GL/glu.h> -#include "config.h" #include "misc.h" #include "Game.h" #include "Support.h" @@ -90,7 +89,8 @@ Game* makeGame(Config config) psychicaimkey = GLFW_KEY_Q; psychickey = GLFW_KEY_Z; - game->customlevels = config.custom_levels; + game->levels = config.levels.ptr; + game->nummissions = config.levels.len; game->debug = config.debug; // TODO: Read high score @@ -240,228 +240,22 @@ void initGame(Game* game) // Level setup game->killedinnocent = 0; // haven't shot any civilians yet... - - if (game->customlevels) { - game->nummissions = 1; // default level in case of load failure - game->type = randomshoot_type; - game->possiblegun[0] = handgun1; - game->possiblegun[1] = handgun2; - game->possiblegun[2] = shotgun; - game->numpossibleguns = 3; - game->evilprobability = 6; - bodyguard.whichgun = knife; - bodyguard.reloads[bodyguard.whichgun] = 6; - if (!game->gameinprogress) - game->score = 0; - game->timeremaining = 50; - game->difficulty= 0.8f; - - std::ifstream ipstream {"data/customlevels.txt"}; - if (ipstream) { - ipstream.ignore(256,'\n');//ignore descriptive text - ipstream >> game->nummissions; - for (int j = 0; j <= game->mission; ++j) { - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> game->type; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> environment; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> game->numpossibleguns; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - - for (int i = 0; i < game->numpossibleguns; ++i) - ipstream >> game->possiblegun[i]; - - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> game->evilprobability; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> bodyguard.whichgun; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> bodyguard.reloads[bodyguard.whichgun]; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> game->timeremaining; - ipstream.ignore(256,'\n'); - ipstream.ignore(256,'\n'); - ipstream >> game->difficulty; - ipstream.ignore(256,'\n'); - } - ipstream.close(); - } else { - game->customlevels = 0; - } - } - - if (!game->customlevels) { - game->nummissions = 13; - if (!game->gameinprogress) - game->score = 0; - - switch (game->mission) { - case 0: - environment = sunny_environment; - game->type = randomshoot_type; - game->possiblegun[0] = handgun1; - game->possiblegun[1] = handgun2; - game->possiblegun[2] = shotgun; - game->numpossibleguns = 3; - game->evilprobability = 6; - bodyguard.whichgun = assaultrifle; - bodyguard.reloads[bodyguard.whichgun] = 6; - game->timeremaining = 50; - game->difficulty = 0.6f; - break; - case 1: - environment = snowy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = knife; - game->possiblegun[1] = assaultrifle; - game->numpossibleguns = 2; - game->evilprobability = 5; - bodyguard.whichgun = handgun2; - bodyguard.reloads[bodyguard.whichgun] = 3; - game->timeremaining = 40; - game->difficulty = 0.6f; - break; - case 2: - environment = foggy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = sniperrifle; - game->numpossibleguns = 1; - game->evilprobability = 5; - bodyguard.whichgun = sniperrifle; - bodyguard.reloads[bodyguard.whichgun] = 4; - game->timeremaining = 50; - game->difficulty = 0.9f; - break; - case 3: - environment = firey_environment; - game->type = zombie_type; - game->numpossibleguns = 0; - game->evilprobability = 5; - bodyguard.whichgun = shotgun; - bodyguard.reloads[bodyguard.whichgun] = 5; - game->timeremaining = 35; - game->difficulty = 0.7f; - break; - case 4: - environment = snowy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = sniperrifle; - game->possiblegun[1] = assaultrifle; - game->numpossibleguns = 2; - game->evilprobability = 5; - bodyguard.whichgun = grenade; - bodyguard.reloads[bodyguard.whichgun] = 20; - game->timeremaining = 30; - game->difficulty = 0.5f; - break; - case 5: - environment = rainy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = handgun1; - game->possiblegun[1] = shotgun; - game->possiblegun[2] = assaultrifle; - game->numpossibleguns = 3; - game->evilprobability = 6; - bodyguard.whichgun = knife; - bodyguard.reloads[bodyguard.whichgun] = 3; - game->timeremaining = 40; - game->difficulty = 0.8f; - break; - case 6: - environment = night_environment; - game->type = randomshoot_type; - game->possiblegun[1] = handgun1; - game->possiblegun[2] = handgun2; - game->possiblegun[3] = shotgun; - game->numpossibleguns = 3; - game->evilprobability = 5; - bodyguard.whichgun = handgun1; - bodyguard.reloads[bodyguard.whichgun] = 4; - game->timeremaining = 30; - game->difficulty = 1.0f; - break; - case 7: - environment = firey_environment; - game->type = zombie_type; - bodyguard.whichgun = assaultrifle; - bodyguard.reloads[bodyguard.whichgun] = 5; - game->timeremaining = 30; - game->difficulty = 1.0f; - break; - case 8: - environment = rainy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = handgun1; - game->possiblegun[1] = handgun2; - game->possiblegun[2] = shotgun; - game->possiblegun[3] = sniperrifle; - game->possiblegun[4] = assaultrifle; - game->numpossibleguns = 5; - game->evilprobability = 5; - bodyguard.whichgun = nogun; - bodyguard.reloads[bodyguard.whichgun] = 3; - game->timeremaining = 40; - game->difficulty = 0.8f; - break; - case 9: - environment = snowy_environment; - game->type = randomshoot_type; - game->possiblegun[0] = knife; - game->possiblegun[1] = handgun1; - game->possiblegun[2] = handgun2; - game->possiblegun[3] = shotgun; - game->possiblegun[4] = sniperrifle; - game->possiblegun[5] = assaultrifle; - game->numpossibleguns = 6; - game->evilprobability = 4; - bodyguard.whichgun = handgun1; - bodyguard.reloads[bodyguard.whichgun] = 3; - game->timeremaining = 90; - game->difficulty = 1.0f; - break; - case 10: - environment = night_environment; - game->type = randomshoot_type; - game->possiblegun[0] = sniperrifle; - game->numpossibleguns = 1; - game->evilprobability = 5; - bodyguard.whichgun = sniperrifle; - bodyguard.reloads[bodyguard.whichgun] = 4; - game->timeremaining = 30; - game->difficulty = 1.3f; - break; - case 11: - environment = sunny_environment; - game->type = randomshoot_type; - game->possiblegun[0] = knife; - game->possiblegun[1] = sniperrifle; - game->numpossibleguns = 2; - game->evilprobability = 4; - bodyguard.whichgun = knife; - game->timeremaining = 30; - game->difficulty = 1.5f; - break; - case 12: - environment = firey_environment; - game->type = zombie_type; - game->possiblegun[0] = knife; - game->possiblegun[1] = sniperrifle; - bodyguard.whichgun = handgun2; - bodyguard.reloads[bodyguard.whichgun] = 10; - game->timeremaining = 60; - game->difficulty = 1.5f; - break; - } - } + if (!game->gameinprogress) + game->score = 0; + auto& level = game->levels[game->mission]; + environment = level.environment; + game->type = (level.evil_weapons & 1 << nogun) + ? zombie_type : randomshoot_type; + game->numpossibleguns = 0; + for (auto weapon : { sniperrifle, assaultrifle, handgun1, + handgun2, shotgun, grenade, knife }) + if (level.evil_weapons & 1 << weapon) + game->possiblegun[game->numpossibleguns++] = weapon; + game->evilprobability = level.evil_rarity; + bodyguard.whichgun = level.guard_weapon; + bodyguard.reloads[bodyguard.whichgun] = level.guard_reloads; + game->timeremaining = level.time; + game->difficulty = level.difficulty; //Setup fast radian to degree conversion rad2deg= 56.54866776; @@ -509,7 +303,6 @@ void initGame(Game* game) alSourcePlay(sound_source); alSourcef(sound_source, AL_MIN_GAIN, 1); alSourcef(sound_source, AL_MAX_GAIN, 1); - } // Setup random seed diff --git a/src/config.h b/src/config.h index 693ca69..f6d7283 100644 --- a/src/config.h +++ b/src/config.h @@ -4,6 +4,16 @@ #include <stdbool.h> #include <stdint.h> +struct Level { + int environment; + unsigned char evil_weapons; + unsigned char evil_rarity; + unsigned char guard_weapon; + unsigned char guard_reloads; + int time; + float difficulty; +}; + struct Config { int width; int height; @@ -15,7 +25,10 @@ struct Config { float mouse_sensitivity; - bool custom_levels; + struct { + struct Level *ptr; + size_t len; + } levels; bool debug; }; diff --git a/src/config.zig b/src/config.zig index dbe0f7d..4a1840d 100644 --- a/src/config.zig +++ b/src/config.zig @@ -16,19 +16,116 @@ // You should have received a copy of the GNU General Public License // along with Black Shades. If not, see <https://www.gnu.org/licenses/>. +const Dir = std.fs.Dir; +const File = std.fs.File; const allocator = std.heap.c_allocator; -const createFile = std.fs.createFileAbsolute; +const c = @import("main.zig").c; const cwd = std.fs.cwd; const data_dir = @import("build_options").data_dir; const eql = std.mem.eql; const ini = @import("ini"); const join = std.fs.path.join; +const maxInt = std.math.maxInt; const mkdir = std.os.mkdir; -const openFile = std.fs.openFileAbsolute; const parseBool = @import("misc.zig").parseBool; const parseFloat = std.fmt.parseFloat; const parseInt = std.fmt.parseInt; const std = @import("std"); +const tokenize = std.mem.tokenize; + +const default_levels_len = 13; + +/// Open the given file for reading, +/// on error copy it from the game's data and try again. +fn openData(dir: Dir, filename: []const u8) !File { + return dir.openFile(filename, .{}) catch { + var data = try cwd().makeOpenPath(data_dir, .{}); + defer data.close(); + try data.copyFile(filename, dir, filename, .{}); + return try dir.openFile(filename, .{}); + }; +} + +const Level = extern struct { + environment: c_int, + evil_weapons: u8, + evil_rarity: u8, + guard_weapon: u8, + guard_reloads: u8, + time: c_int, + difficulty: f32, +}; + +const Weapons = packed struct { + // TODO: remove nogun and sort + jaw: bool = false, + sniper_rifle: bool = false, + assault_rifle: bool = false, + magnum: bool = false, + handgun: bool = false, + grenade: bool = false, + knife: bool = false, + shotgun: bool = false, +}; + +fn parseLevels(dir_path: []const u8, length: usize) ![*]Level { + var dir = try cwd().makeOpenPath(dir_path, .{}); + defer dir.close(); + const input = try openData(dir, "levels.ini"); + defer input.close(); + const levels = try allocator.alloc(Level, length); + errdefer allocator.free(levels); + var parser = ini.parse(allocator, input.reader()); + defer parser.deinit(); + + var i: usize = maxInt(usize); + while (try parser.next()) |record| + switch (record) { + .section => i +%= 1, + .property => |kv| if (eql(u8, kv.key, "environment")) { + levels[i].environment = if (eql(u8, kv.value, "sunny")) + c.sunny_environment + else if (eql(u8, kv.value, "foggy")) + c.foggy_environment + else if (eql(u8, kv.value, "snowy")) + c.snowy_environment + else if (eql(u8, kv.value, "rainy")) + c.rainy_environment + else if (eql(u8, kv.value, "firey")) + c.firey_environment + else if (eql(u8, kv.value, "night")) + c.night_environment + else return error.InvalidData; + } else if (eql(u8, kv.key, "evil weapons")) { + var weapons = Weapons{}; + var enums = tokenize(kv.value, " "); + while (enums.next()) |weapon| + switch (try parseInt(u3, weapon, 10)) { + c.nogun => weapons.jaw = true, + c.sniperrifle => weapons.sniper_rifle = true, + c.assaultrifle => weapons.assault_rifle = true, + c.handgun1 => weapons.magnum = true, + c.handgun2 => weapons.handgun = true, + c.grenade => weapons.grenade = true, + c.knife => weapons.knife = true, + c.shotgun => weapons.shotgun = true, + }; + levels[i].evil_weapons = @bitCast(u8, weapons); + } else if (eql(u8, kv.key, "evil rarity")) { + levels[i].evil_rarity = try parseInt(u8, kv.value, 10); + } else if (eql(u8, kv.key, "guard weapon")) { + levels[i].guard_weapon = try parseInt(u3, kv.value, 10); + } else if (eql(u8, kv.key, "guard reloads")) { + levels[i].guard_reloads = try parseInt(u8, kv.value, 10); + } else if (eql(u8, kv.key, "time")) { + levels[i].time = try parseInt(c_int, kv.value, 10); + } else if (eql(u8, kv.key, "difficulty")) { + levels[i].difficulty = try parseFloat(f32, kv.value); + } else return error.InvalidData, + else => return error.InvalidData, + }; + return levels.ptr; +} /// Game configuration. pub const Config = extern struct { @@ -42,7 +139,10 @@ pub const Config = extern struct { mouse_sensitivity: f32 = 1.0, - custom_levels: bool = false, + levels: extern struct { + ptr: [*]Level, + len: usize, + } = .{ .ptr = undefined, .len = 0 }, debug: bool = false, }; @@ -52,19 +152,12 @@ pub fn parse(base_dir: []const u8) !Config { defer allocator.free(config_dir); var dir = try cwd().makeOpenPath(config_dir, .{}); defer dir.close(); - - var config = Config{}; - const input = dir.openFile("config.ini", .{}) catch blk: { - var source = try cwd().makeOpenPath(data_dir, .{}); - defer source.close(); - try source.copyFile("config.ini", dir, "config.ini", .{}); - break :blk try dir.openFile("config.ini", .{}); - }; + const input = try openData(dir, "config.ini"); defer input.close(); - var parser = ini.parse(allocator, input.reader()); defer parser.deinit(); + var config = Config{}; var section: []u8 = ""; defer allocator.free(section); while (try parser.next()) |record| @@ -84,23 +177,30 @@ pub fn parse(base_dir: []const u8) !Config { config.blur = try parseBool(kv.value) else if (eql(u8, kv.key, "blood")) config.blood = try parseBool(kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "audio")) { if (eql(u8, kv.key, "music")) config.music = try parseBool(kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "input")) { if (eql(u8, kv.key, "mouse sensitivity")) config.mouse_sensitivity = try parseFloat(f32, kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "misc")) { if (eql(u8, kv.key, "custom levels")) - config.custom_levels = try parseBool(kv.value) + config.levels.len = try parseInt(usize, kv.value, 10) else if (eql(u8, kv.key, "debug")) config.debug = try parseBool(kv.value) - else unreachable; - } else unreachable, - else => unreachable, + else return error.InvalidData; + } else return error.InvalidData, + else => return error.InvalidData, }; + + if (config.levels.len > 0) { + config.levels.ptr = try parseLevels(config_dir, config.levels.len); + } else { + config.levels.len = default_levels_len; + config.levels.ptr = try parseLevels(data_dir, config.levels.len); + } return config; } diff --git a/src/main.zig b/src/main.zig index d40f012..11775ec 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,17 +16,20 @@ // You should have received a copy of the GNU General Public License // along with Black Shades. If not, see <https://www.gnu.org/licenses/>. -const Loca = @import("loca").Loca; -const al = @import("zeal"); -const allocator = @import("std").heap.c_allocator; -const c = @cImport({ +pub const c = @cImport({ @cInclude("Game.h"); @cInclude("Constants.h"); }); + +const Loca = @import("loca").Loca; +const al = @import("zeal"); +const allocator = std.heap.c_allocator; const configuration = @import("config.zig"); +const free = std.c.free; const gf = @import("gfz"); const gl = @import("zgl"); const misc = @import("misc.zig"); +const std = @import("std"); var game: *c.Game = undefined; @@ -53,6 +56,7 @@ pub fn main() !void { const loca = try Loca.init(allocator, .{}); defer loca.deinit(); const config = try configuration.parse(loca.user_config); + defer free(config.levels.ptr); try gf.init(); defer gf.deinit() catch unreachable; |