diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index b480d80723..38e1118dbe 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -41,6 +41,7 @@
+
@@ -94,6 +95,7 @@
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index 5f0581eca7..b1610b9290 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -174,6 +174,9 @@
Header Files
+
+ Header Files
+
@@ -425,6 +428,9 @@
Windows
+
+ Source Files
+
diff --git a/src/addresses.h b/src/addresses.h
index 00f77551f9..56dd5fa803 100644
--- a/src/addresses.h
+++ b/src/addresses.h
@@ -285,6 +285,9 @@
#define RCT2_ADDRESS_SECURITY_COLOUR 0x01357BCF
#define RCT2_ADDRESS_ACTIVE_RESEARCH_TYPES 0x01357CF2
+#define RCT2_ADDRESS_RESEARH_PROGRESS_STAGE 0x01357CF3
+
+#define RCT2_ADDRESS_RESEARH_PROGRESS 0x013580E4
#define RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY 0x013580E7
#define RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_MONTH 0x013580E8
diff --git a/src/game.c b/src/game.c
index 8348b053cd..34d32ddfa2 100644
--- a/src/game.c
+++ b/src/game.c
@@ -31,6 +31,7 @@
#include "park.h"
#include "peep.h"
#include "rct2.h"
+#include "research.h"
#include "ride.h"
#include "sawyercoding.h"
#include "scenario.h"
@@ -494,7 +495,7 @@ void game_logic_update()
RCT2_CALLPROC_EBPSAFE(0x00672AA4); // update text effects
RCT2_CALLPROC_EBPSAFE(0x006ABE4C); // update rides
park_update();
- RCT2_CALLPROC_EBPSAFE(0x00684C7A); // update research
+ research_update();
RCT2_CALLPROC_EBPSAFE(0x006B5A2A); // update ride ratings
ride_measurements_update();
RCT2_CALLPROC_EBPSAFE(0x0068AFAD);
diff --git a/src/research.c b/src/research.c
new file mode 100644
index 0000000000..ab7e3b2cf2
--- /dev/null
+++ b/src/research.c
@@ -0,0 +1,131 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Ted John
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * This file is part of OpenRCT2.
+ *
+ * OpenRCT2 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 3 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, see .
+ *****************************************************************************/
+
+#include "addresses.h"
+#include "date.h"
+#include "research.h"
+#include "rct2.h"
+#include "window.h"
+
+typedef struct {
+ sint32 var_0;
+ uint8 category;
+} rct_research_item;
+
+const int _researchRate[] = { 0, 160, 250, 400 };
+
+// 0x00EE787C
+uint8 gResearchUncompletedCategories;
+
+/**
+ *
+ * rct2: 0x00684BAE
+ */
+void research_update_uncompleted_types()
+{
+ int uncompletedResearchTypes = 0;
+ rct_research_item *researchItem = (rct_research_item*)0x001358844;
+ while (researchItem->var_0 != -1)
+ researchItem++;
+ researchItem++;
+ for (; researchItem->var_0 != -2; researchItem++)
+ uncompletedResearchTypes |= (1 << researchItem->category);
+
+ gResearchUncompletedCategories = uncompletedResearchTypes;
+}
+
+/**
+ *
+ * rct2: 0x00684D2A
+ */
+static void research_calculate_expected_date()
+{
+ int progress = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16);
+ int progressStage = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8);
+ int researchLevel = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RESEARCH_LEVEL, uint8);
+ int currentDay = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_TICKS, uint16);
+ int currentMonth = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16);
+ int expectedDay, expectedMonth, dayQuotient, dayRemainder, progressRemaining, daysRemaining;
+
+ if (progressStage == RESEARCH_STAGE_INITIAL_RESEARCH || researchLevel == RESEARCH_FUNDING_NONE) {
+ RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY, uint8) = 255;
+ } else {
+ progressRemaining = progressStage == RESEARCH_STAGE_COMPLETING_DESIGN ? 0x10000 : 0x20000;
+ progressRemaining -= progress;
+ daysRemaining = (progressRemaining / _researchRate[researchLevel]) * 128;
+
+ expectedDay = currentDay + (daysRemaining & 0xFFFF);
+ dayQuotient = expectedDay / 0x10000;
+ dayRemainder = expectedDay % 0x10000;
+
+ expectedMonth = date_get_month(currentMonth + dayQuotient + (daysRemaining >> 16));
+ expectedDay = (dayRemainder * days_in_month[expectedMonth]) >> 16;
+
+ RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY, uint8) = expectedDay;
+ RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_MONTH, uint8) = expectedMonth;
+ }
+}
+
+/**
+ *
+ * rct2: 0x00684C7A
+ */
+void research_update()
+{
+ int editorScreenFlags, researchLevel, currentResearchProgress;
+
+ editorScreenFlags = SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER;
+ if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & editorScreenFlags)
+ return;
+
+ if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32) % 32 != 0)
+ return;
+
+ researchLevel = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RESEARCH_LEVEL, uint8);
+
+ currentResearchProgress = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16);
+ currentResearchProgress += _researchRate[researchLevel];
+ if (currentResearchProgress <= 0xFFFF) {
+ RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = currentResearchProgress;
+ } else {
+ switch (RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8)) {
+ case RESEARCH_STAGE_INITIAL_RESEARCH:
+ RCT2_CALLPROC_EBPSAFE(0x00684BE5);
+ research_calculate_expected_date();
+ break;
+ case RESEARCH_STAGE_DESIGNING:
+ RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
+ RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = RESEARCH_STAGE_COMPLETING_DESIGN;
+ research_calculate_expected_date();
+ window_invalidate_by_id(WC_CONSTRUCT_RIDE, 0);
+ window_invalidate_by_id(WC_RESEARCH, 0);
+ break;
+ case RESEARCH_STAGE_COMPLETING_DESIGN:
+ RCT2_CALLPROC_X(0x006848D4, RCT2_GLOBAL(0x013580E0, uint32), 0, 0, 0, 0, 0, 0);
+ RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
+ RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = 0;
+ research_calculate_expected_date();
+ research_update_uncompleted_types();
+ window_invalidate_by_id(WC_CONSTRUCT_RIDE, 0);
+ window_invalidate_by_id(WC_RESEARCH, 0);
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/research.h b/src/research.h
new file mode 100644
index 0000000000..fbfb5c3f08
--- /dev/null
+++ b/src/research.h
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Ted John
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * This file is part of OpenRCT2.
+ *
+ * OpenRCT2 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 3 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, see .
+ *****************************************************************************/
+
+#ifndef _RESEARCH_H_
+#define _RESEARCH_H_
+
+enum {
+ RESEARCH_FUNDING_NONE,
+ RESEARCH_FUNDING_MINIMUM,
+ RESEARCH_FUNDING_NORMAL,
+ RESEARCH_FUNDING_MAXIMUM
+};
+
+enum {
+ RESEARCH_STAGE_INITIAL_RESEARCH,
+ RESEARCH_STAGE_DESIGNING,
+ RESEARCH_STAGE_COMPLETING_DESIGN,
+ RESEARCH_STAGE_UNKNOWN
+};
+
+extern uint8 gResearchUncompletedCategories;
+
+void research_update_uncompleted_types();
+void research_update();
+
+#endif
\ No newline at end of file
diff --git a/src/window_finances.c b/src/window_finances.c
index 56f7072898..b3ed322b4e 100644
--- a/src/window_finances.c
+++ b/src/window_finances.c
@@ -24,6 +24,7 @@
#include "game.h"
#include "graph.h"
#include "marketing.h"
+#include "research.h"
#include "ride.h"
#include "scenario.h"
#include "string_ids.h"
@@ -535,7 +536,7 @@ void window_finances_open()
w->colours[0] = 1;
w->colours[1] = 19;
w->colours[2] = 19;
- RCT2_CALLPROC_EBPSAFE(0x00684BAE);
+ research_update_uncompleted_types();
}
w->page = 0;
@@ -1434,7 +1435,7 @@ static void window_finances_research_invalidate()
// Checkboxes
int activeResearchTypes = RCT2_GLOBAL(RCT2_ADDRESS_ACTIVE_RESEARCH_TYPES, uint16);
- int uncompletedResearchTypes = RCT2_GLOBAL(RCT2_ADDRESS_UNCOMPLETED_RESEARCH_TYPES, uint16);
+ int uncompletedResearchTypes = gResearchUncompletedCategories;
for (int i = 0; i < 7; i++) {
int mask = 1 << i;
int widgetMask = 1 << (i + WIDX_TRANSPORT_RIDES);
diff --git a/src/window_research.c b/src/window_research.c
index 5685c80da1..cd79abc2ca 100644
--- a/src/window_research.c
+++ b/src/window_research.c
@@ -22,6 +22,7 @@
#include "finance.h"
#include "game.h"
#include "news_item.h"
+#include "research.h"
#include "ride.h"
#include "scenery.h"
#include "string_ids.h"
@@ -236,7 +237,7 @@ void window_research_open()
w->colours[0] = 1;
w->colours[1] = 19;
w->colours[2] = 19;
- RCT2_CALLPROC_EBPSAFE(0x00684BAE);
+ research_update_uncompleted_types();
}
w->page = 0;
@@ -339,9 +340,9 @@ static void window_research_development_paint()
// Research type
stringId = STR_RESEARCH_UNKNOWN;
- if (RCT2_GLOBAL(0x01357CF3, uint8) != 0) {
+ if (RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) != 0) {
stringId = STR_TRANSPORT_RIDE + RCT2_GLOBAL(0x013580E6, uint8);
- if (RCT2_GLOBAL(0x01357CF3, uint8) != 1) {
+ if (RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) != 1) {
uint32 typeId = RCT2_GLOBAL(0x013580E0, uint32);
if (typeId >= 0x10000) {
rct_ride_type *rideEntry = RCT2_GLOBAL(0x009ACFA4 + (typeId & 0xFF) * 4, rct_ride_type*);
@@ -357,13 +358,13 @@ static void window_research_development_paint()
y += 25;
// Progress
- stringId = 2285 + RCT2_GLOBAL(0x01357CF3, uint8);
+ stringId = 2285 + RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8);
gfx_draw_string_left_wrapped(dpi, &stringId, x, y, 296, STR_RESEARCH_PROGRESS_LABEL, 0);
y += 15;
// Expected
RCT2_GLOBAL(0x013CE952, uint16) = STR_UNKNOWN;
- if (RCT2_GLOBAL(0x01357CF3, uint8) != 0) {
+ if (RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) != 0) {
uint16 expectedDay = RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY, uint8);
if (expectedDay != 255) {
RCT2_GLOBAL(0x013CE952 + 2, uint16) = STR_DATE_DAY_1 + expectedDay;
@@ -529,13 +530,13 @@ static void window_research_funding_invalidate()
// Checkboxes
int activeResearchTypes = RCT2_GLOBAL(RCT2_ADDRESS_ACTIVE_RESEARCH_TYPES, uint16);
- int uncompletedResearchTypes = RCT2_GLOBAL(RCT2_ADDRESS_UNCOMPLETED_RESEARCH_TYPES, uint16);
+ int uncompletedResearchTypes = gResearchUncompletedCategories;
for (int i = 0; i < 7; i++) {
int mask = 1 << i;
int widgetMask = 1 << (i + WIDX_TRANSPORT_RIDES);
// Set checkbox disabled if research type is complete
- if (uncompletedResearchTypes & mask) {
+ if (gResearchUncompletedCategories & mask) {
w->disabled_widgets &= ~widgetMask;
// Set checkbox ticked if research type is active