Skip to content

Commit

Permalink
Start adding support for custom campaigns
Browse files Browse the repository at this point in the history
Create the "New campaign" menu, which replaces the former "Start new career"
  • Loading branch information
crudelios committed Mar 12, 2024
1 parent faa8b55 commit be1be6f
Show file tree
Hide file tree
Showing 16 changed files with 506 additions and 144 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ set(FIGURETYPE_FILES
set(GAME_FILES
${PROJECT_SOURCE_DIR}/src/game/animation.c
${PROJECT_SOURCE_DIR}/src/game/cheats.c
${PROJECT_SOURCE_DIR}/src/game/custom_campaign.c
${PROJECT_SOURCE_DIR}/src/game/difficulty.c
${PROJECT_SOURCE_DIR}/src/game/file.c
${PROJECT_SOURCE_DIR}/src/game/file_editor.c
Expand Down Expand Up @@ -635,7 +636,7 @@ set(WINDOW_FILES
${PROJECT_SOURCE_DIR}/src/window/mission_briefing.c
${PROJECT_SOURCE_DIR}/src/window/mission_end.c
${PROJECT_SOURCE_DIR}/src/window/mission_selection.c
${PROJECT_SOURCE_DIR}/src/window/new_career.c
${PROJECT_SOURCE_DIR}/src/window/new_campaign.c
${PROJECT_SOURCE_DIR}/src/window/numeric_input.c
${PROJECT_SOURCE_DIR}/src/window/option_popup.c
${PROJECT_SOURCE_DIR}/src/window/overlay_menu.c
Expand Down
196 changes: 196 additions & 0 deletions src/game/custom_campaign.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#include "custom_campaign.h"

#include "core/encoding.h"
#include "core/file.h"
#include "core/log.h"
#include "core/xml_parser.h"

#include "zip/zip.h"

#define XML_TOTAL_ELEMENTS 5

static int xml_start_campaign(void);
static int xml_start_intro(void);
static void xml_intro_text(const char *text);
static int xml_start_missions(void);
static int xml_start_mission(void);
static int xml_start_option(void);

static void xml_end_mission(void);

static struct {
int success;
custom_campaign_info campaign;
char file_name[FILE_NAME_MAX];
} data;

static const xml_parser_element xml_elements[XML_TOTAL_ELEMENTS] = {
{ "campaign", xml_start_campaign },
{ "intro", xml_start_intro, 0, "campaign", xml_intro_text },
{ "missions", xml_start_missions, 0, "campaign" },
{ "mission", xml_start_mission, xml_end_mission, "missions" },
{ "option", xml_start_option, 0, "mission" }
};


static int get_info(const char *xml, unsigned int size)
{
data.success = 1;

if (!xml_parser_init(xml_elements, XML_TOTAL_ELEMENTS)) {
return 0;
}
if (!xml_parser_parse(xml, size, 1)) {
data.success = 0;
}
xml_parser_free();

return data.success;
}

static int xml_start_campaign(void)
{
return xml_parser_get_attribute_int("version") == 1;
}

static const uint8_t *copy_string_from_xml(const char *text)
{
if (!text || !*text) {
return 0;
}
size_t length = strlen(text) + 1;
uint8_t *result = malloc(sizeof(uint8_t) * length);
if (!result) {
return 0;
}
encoding_from_utf8(text, result, (int) length);
return result;
}

static int xml_start_intro(void)
{
data.campaign.name = copy_string_from_xml(xml_parser_get_attribute_string("title"));
return 1;
}

static void xml_intro_text(const char *text)
{
data.campaign.description = copy_string_from_xml(text);
}

static int xml_start_missions(void)
{
return 1;
}

static int xml_start_mission(void)
{
return 1;
}

static int xml_start_option(void)
{
return 1;
}

static void xml_end_mission(void)
{
}

static char *extract_xml(struct zip_t *zip, unsigned int *size)
{
*size = 0;
if (zip_entry_open(zip, "settings.xml") < 0) {
log_error("Unable to open campaign file - invalid file format", 0, 0);
return 0;
}

size_t xml_size = zip_entry_size(zip);
char *xml_text = malloc(xml_size);

if (!xml_text) {
log_error("Not enough memory to obtain the xml entry from the file", 0, 0);
zip_entry_close(zip);
return 0;
}

zip_entry_noallocread(zip, xml_text, xml_size);
zip_entry_close(zip);

*size = (unsigned int) xml_size;

return xml_text;
}

const custom_campaign_info *custom_campaign_load_info(const char *filename)
{
if (!filename || !*filename) {
return 0;
}

if (strcmp(filename, data.file_name) == 0) {
return &data.campaign;
}

custom_campaign_clear();

log_info("Opening campaign file:", filename, 0);

FILE *zip_stream = file_open(filename, "rb");

if (!zip_stream) {
log_error("Unable to open campaign file - file does not exist", 0, 0);
return 0;
}

struct zip_t *zip = zip_cstream_open(zip_stream, 0, 'r');
if (!zip) {
log_error("Unable to open campaign file - invalid file format", 0, 0);
return 0;
}

unsigned int xml_size;
char *xml_text = extract_xml(zip, &xml_size);

if (!xml_text) {
zip_close(zip);
file_close(zip_stream);

return 0;
}

if (!get_info(xml_text, xml_size)) {
log_error("Unable to open campaign settings - invalid file format", 0, 0);

free(xml_text);

zip_entry_close(zip);
zip_close(zip);
file_close(zip_stream);

return 0;
}

free(xml_text);

zip_entry_close(zip);
zip_close(zip);
file_close(zip_stream);

strncpy(data.file_name, filename, FILE_NAME_MAX);

return &data.campaign;
}

void custom_campaign_clear(void)
{
if (data.campaign.name) {
free((uint8_t *) data.campaign.name);
}
if (data.campaign.description) {
free((uint8_t *) data.campaign.description);
}
memset(&data.campaign, 0, sizeof(data.campaign));

data.file_name[0] = 0;
}
16 changes: 16 additions & 0 deletions src/game/custom_campaign.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef GAME_CUSTOM_CAMPAIGN_H
#define GAME_CUSTOM_CAMPAIGN_H

#include <stdint.h>

typedef struct {
const uint8_t *name;
const uint8_t *description;
int number_of_missions;
} custom_campaign_info;

const custom_campaign_info *custom_campaign_load_info(const char *filename);

void custom_campaign_clear(void);

#endif // GAME_CUSTOM_CAMPAIGN_H
2 changes: 1 addition & 1 deletion src/graphics/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ typedef enum {
WINDOW_CONFIG,
WINDOW_HOTKEY_CONFIG,
WINDOW_HOTKEY_EDITOR,
WINDOW_NEW_CAREER,
WINDOW_NEW_CAMPAIGN,
WINDOW_CCK_SELECTION,
WINDOW_FILE_DIALOG,
WINDOW_POPUP_DIALOG,
Expand Down
2 changes: 2 additions & 0 deletions src/platform/augustus.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,8 @@ int main(int argc, char **argv)

setup(&args);



mouse_set_inside_window(1);
run_and_draw();

Expand Down
12 changes: 8 additions & 4 deletions src/scenario/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,16 +251,20 @@ extern struct scenario_t {
} native_images;

struct { // used to be stored in the settings file
int campaign_rank;
int campaign_mission;
int is_custom;
int starting_favor;
int starting_personal_savings;
uint8_t player_name[MAX_PLAYER_NAME];
/** Temp storage for carrying over player name to next campaign mission */
uint8_t campaign_player_name[MAX_PLAYER_NAME];
} settings;

struct {
int rank;
int mission;
char custom_name[FILE_NAME_MAX];
/** Temp storage for carrying over player name to next campaign mission */
uint8_t player_name[MAX_PLAYER_NAME];
} campaign;

int is_saved;
} scenario;

Expand Down
23 changes: 14 additions & 9 deletions src/scenario/property.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,42 @@ void scenario_set_custom(int custom)

int scenario_campaign_rank(void)
{
return scenario.settings.campaign_rank;
return scenario.campaign.rank;
}

void scenario_set_campaign_rank(int rank)
{
scenario.settings.campaign_rank = rank;
scenario.campaign.rank = rank;
}

int scenario_campaign_mission(void)
{
return scenario.settings.campaign_mission;
return scenario.campaign.mission;
}

void scenario_set_campaign_mission(int mission)
{
scenario.settings.campaign_mission = mission;
scenario.campaign.mission = mission;
}

static int is_custom_campaign(void)
{
return scenario.campaign.custom_name[0] != 0;
}

int scenario_is_tutorial_1(void)
{
return !scenario.settings.is_custom && scenario.settings.campaign_rank == 0;
return !scenario.settings.is_custom && scenario.campaign.rank == 0 && !is_custom_campaign();
}

int scenario_is_tutorial_2(void)
{
return !scenario.settings.is_custom && scenario.settings.campaign_rank == 1;
return !scenario.settings.is_custom && scenario.campaign.rank == 1 && !is_custom_campaign();
}

int scenario_is_tutorial_3(void)
{
return !scenario.settings.is_custom && scenario.settings.campaign_rank == 2;
return !scenario.settings.is_custom && scenario.campaign.rank == 2 && !is_custom_campaign();
}

int scenario_starting_favor(void)
Expand Down Expand Up @@ -82,12 +87,12 @@ void scenario_set_player_name(const uint8_t *name)

void scenario_save_campaign_player_name(void)
{
string_copy(scenario.settings.player_name, scenario.settings.campaign_player_name, MAX_PLAYER_NAME);
string_copy(scenario.settings.player_name, scenario.campaign.player_name, MAX_PLAYER_NAME);
}

void scenario_restore_campaign_player_name(void)
{
string_copy(scenario.settings.campaign_player_name, scenario.settings.player_name, MAX_PLAYER_NAME);
string_copy(scenario.campaign.player_name, scenario.settings.player_name, MAX_PLAYER_NAME);
}

int scenario_is_open_play(void)
Expand Down
16 changes: 9 additions & 7 deletions src/scenario/scenario.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,10 @@ void scenario_map_data_from_buffer(buffer *buf, int *width, int *height, int *gr

void scenario_settings_init(void)
{
scenario.settings.campaign_mission = 0;
scenario.settings.campaign_rank = 0;
scenario.campaign.mission = 0;
scenario.campaign.rank = 0;
scenario.campaign.player_name[0] = 0;
scenario.campaign.custom_name[0] = 0;
scenario.settings.is_custom = 0;
scenario.settings.starting_favor = difficulty_starting_favor();
scenario.settings.starting_personal_savings = 0;
Expand All @@ -764,7 +766,7 @@ void scenario_settings_init_mission(void)
{
scenario.settings.starting_favor = difficulty_starting_favor();
scenario.settings.starting_personal_savings =
setting_personal_savings_for_mission(scenario.settings.campaign_rank);
setting_personal_savings_for_mission(scenario.campaign.rank);
}

void scenario_unlock_all_buildings(void)
Expand All @@ -777,11 +779,11 @@ void scenario_unlock_all_buildings(void)

void scenario_settings_save_state(buffer *part1, buffer *part2, buffer *part3, buffer *player_name, buffer *scenario_name)
{
buffer_write_i32(part1, scenario.settings.campaign_mission);
buffer_write_i32(part1, scenario.campaign.mission);

buffer_write_i32(part2, scenario.settings.starting_favor);
buffer_write_i32(part2, scenario.settings.starting_personal_savings);
buffer_write_i32(part2, scenario.settings.campaign_rank);
buffer_write_i32(part2, scenario.campaign.rank);

buffer_write_i32(part3, scenario.settings.is_custom);

Expand All @@ -795,11 +797,11 @@ void scenario_settings_save_state(buffer *part1, buffer *part2, buffer *part3, b
void scenario_settings_load_state(
buffer *part1, buffer *part2, buffer *part3, buffer *player_name, buffer *scenario_name)
{
scenario.settings.campaign_mission = buffer_read_i32(part1);
scenario.campaign.mission = buffer_read_i32(part1);

scenario.settings.starting_favor = buffer_read_i32(part2);
scenario.settings.starting_personal_savings = buffer_read_i32(part2);
scenario.settings.campaign_rank = buffer_read_i32(part2);
scenario.campaign.rank = buffer_read_i32(part2);

scenario.settings.is_custom = buffer_read_i32(part3);

Expand Down
Loading

0 comments on commit be1be6f

Please sign in to comment.