-
Notifications
You must be signed in to change notification settings - Fork 83
Weather
The weather is specific to each of 36 quadrants of the provinces, and changes every hour.
season <- getSeason() # March = Spring = 0
for i <- 0, 36
climate <- climates[i]
variant <- <40% for 2, 20% for 1 and 3, 10% for 0 or 4>
weather[i] <- weatherTable[climate*20 + season*5 + variant]climates is a 36 1-byte values @4295E
weatherTable is 140 1-byte values @42990
0 clear
1 overcast, fog
2 rain
3 snow
4 snow overcast, fog
5 rain
6 same as 1?
7 snow overcast, if rnd()<16000 then fog (recalculate on each outside transition)
If rain and rnd()<24000, it is thunderstorm.
If fog and (day number & 8), it is heavy fog.
In terrain type 3 (different from climate above), there is raining instead of snow/snow overcast.
Rain in cities uses W tileset. Overcast and rain in the wilderness use R tileset.
When it is snowing or snow overcast, the following NPCs are missing from cities: fire-eater, prostitutes, and snake enchanter (bugged in the original game).
Lightning bolts and sky flashes can occur >6:01pm and <6:00am.
chance <- rnd()
if chance < 2000 then
lightning <- chance mod 6
angle <- rnd() mod 512
x,y <- polar_to_xy(10000, angle)
# this happens for next 3 frames
paint_sky(flash_color[frame])
X,Y <- project_to_screen(x,y)
blit(lightnings[lightning],X,Y)
# when done, do thunder
play_sound(28, player_position)
endifflash_colors is a 3-byte array @43602
lightnings is an image array of lglit01.cfa, lglit02.cfa, etc.
int16_t CosineTable[641]; // @4A3D6
void RotatePoint(int16_t angle, int16_t& xPos, int16_t& zPos) {
int16_t doubled_xPos = xPos * 2;
int16_t doubled_zPos = zPos * 2;
int32_t imul_res1 = doubled_xPos * CosineTable[angle + 128];
int16_t high_res1 = imul_res1 >> 16;
int16_t multiplier1 = CosineTable[angle];
int16_t neg_multiplier1 = -multiplier1;
bool overflow = (multiplier1 == INT16_MIN);
if (overflow) {
neg_multiplier1--;
}
int32_t imul_res2 = doubled_zPos * neg_multiplier1;
int16_t high_res2 = imul_res2 >> 16;
xPos = high_res2 + high_res1;
int32_t imul_res3 = doubled_xPos * CosineTable[angle];
int16_t high_res3 = imul_res3 >> 16;
int32_t imul_res4 = doubled_zPos * CosineTable[angle + 128];
int16_t high_res4 = imul_res4 >> 16;
zPos = high_res3 + high_res4;
}
int16_t WORD_ARRAY_4b80_81d8[24]; // @47708. Both read from and written to.
int32_t DWORD_4b80_819a = 0;
int32_t DWORD_4b80_819e = 0;
int32_t DWORD_4b80_81a2 = 0;
int32_t DWORD_4b80_81a6 = 0x6906904; // Constant value. @476D6.
int32_t DWORD_4b80_81aa;
uint16_t WORD_4b80_81ae = 0;
uint16_t WORD_4b80_81b0 = 0;
int16_t WORD_4b80_81b2 = 0;
int16_t WORD_4b80_81b4 = 0;
uint16_t WORD_4b80_81b6 = 0;
uint16_t WORD_4b80_81b8 = 0;
int16_t WORD_4b80_81c6 = 0;
int16_t WORD_4b80_81c8 = 0;
int16_t WORD_4b80_81ca = 0;
int16_t WORD_4b80_81d4 = 0xFC00; // Constant value. @47704.
int16_t WORD_4b80_8208 = 0;
int16_t WORD_4b80_a784 = 0x92; // Variable value, but might always be 0x92 when this function is called.
int32_t DWORD_VALUE1;
int32_t DWORD_VALUE2;
int32_t DWORD_VALUE3;
int32_t DWORD_VALUE4;
int32_t DWORD_VALUE5;
int16_t PlayerX;
int16_t PlayerZ;
int16_t PlayerAngle;
uint16_t WORD_4b80_191b = 0;
uint16_t WORD_4b80_191d = 0;
int16_t FOGTXTSample[1045] = { 0 };
void SampleFOGTXT()
{
WORD_4b80_191b += 4;
WORD_4b80_191d += 4;
// First the file FOG.TXT is loaded into memory if it hasn't been already. Omitted here.
int32_t index = 0;
int32_t loopCount = 4;
int32_t FOGTXTSampleIndex = 45;
do {
AX = WORD_ARRAY_4b80_81d8[index];
CX = WORD_ARRAY_4b80_81d8[index + 2];
DI = 511;
DI -= PlayerAngle; // PlayerAngle is never greater than 511
RotatePoint(DI, AX, CX);
BX = WORD_ARRAY_4b80_81d8[index + 1];
WORD_ARRAY_4b80_81d8[index + 3] = AX;
WORD_ARRAY_4b80_81d8[index + 4] = BX;
WORD_ARRAY_4b80_81d8[index + 5] = CX;
index += 6;
loopCount--;
} while (loopCount != 0);
DWORD_4b80_819a = 0xD0300000; // Original game does a few calculations here to get the value, but it will always be this result
DWORD_4b80_81aa = 0xDD5D5D5E; // Original game does a few calculations here to get the value, but it will always be this result
WORD_4b80_8208 = 0;
do {
BP = WORD_4b80_a784; // Seems to always be 0092 when this function is called
BP >>= 3;
AX = WORD_ARRAY_4b80_81d8[15];
AX -= WORD_ARRAY_4b80_81d8[3];
int32_t product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
int32_t dividend = (DX << 16) | AX;
int16_t divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[3];
WORD_4b80_81b0 = AX;
AX = WORD_ARRAY_4b80_81d8[16];
AX -= WORD_ARRAY_4b80_81d8[4];
product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
dividend = (DX << 16) | AX;
divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[4];
WORD_4b80_81b4 = AX;
AX = WORD_ARRAY_4b80_81d8[17];
AX -= WORD_ARRAY_4b80_81d8[5];
product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
dividend = (DX << 16) | AX;
divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[5];
WORD_4b80_81b8 = AX;
WORD_4b80_81ae = 0;
WORD_4b80_81b2 = 0;
WORD_4b80_81b6 = 0;
AX = WORD_ARRAY_4b80_81d8[21];
AX -= WORD_ARRAY_4b80_81d8[9];
product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
dividend = (DX << 16) | AX;
divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[9];
WORD_4b80_81c6 = AX;
AX = WORD_ARRAY_4b80_81d8[22];
AX -= WORD_ARRAY_4b80_81d8[10];
product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
dividend = (DX << 16) | AX;
divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[10];
WORD_4b80_81c8 = AX;
AX = WORD_ARRAY_4b80_81d8[23];
AX -= WORD_ARRAY_4b80_81d8[11];
product = AX * WORD_4b80_8208;
AX = static_cast<int16_t>(product);
DX = product >> 16;
dividend = (DX << 16) | AX;
divisor = BP;
AX = static_cast<int16_t>(dividend / divisor);
DX = dividend % divisor;
AX += WORD_ARRAY_4b80_81d8[11];
WORD_4b80_81ca = AX;
BP = 39;
AX = WORD_4b80_81c6;
AX -= WORD_4b80_81b0;
EAX = AX << 16;
int64_t dividend2 = EAX;
int32_t divisor2 = BP;
EAX = static_cast<int32_t>(dividend2 / divisor2);
EDX = static_cast<int32_t>(dividend2 % divisor2);
DWORD_VALUE3 = EAX;
AX = WORD_4b80_81c8;
AX -= WORD_4b80_81b4;
EAX = AX << 16;
dividend2 = EAX;
divisor2 = BP;
EAX = static_cast<int32_t>(dividend2 / divisor2);
EDX = static_cast<int32_t>(dividend2 % divisor2);
DWORD_VALUE4 = EAX;
AX = WORD_4b80_81ca;
AX -= WORD_4b80_81b8;
EAX = AX << 16;
dividend2 = EAX;
divisor2 = BP;
EAX = static_cast<int32_t>(dividend2 / divisor2);
EDX = static_cast<int32_t>(dividend2 % divisor2);
DWORD_VALUE5 = EAX;
ECX = WORD_4b80_81b4 * WORD_4b80_81d4;
DWORD_4b80_819e = ECX;
EAX = WORD_4b80_81c8 * WORD_4b80_81d4;
EAX -= ECX;
int64_t product2 = static_cast<int64_t>(EAX) * static_cast<int64_t>(DWORD_4b80_81a6);
EAX = static_cast<int32_t>(product2);
EDX = product2 >> 32;
DWORD_VALUE1 = EAX;
DWORD_VALUE2 = EDX;
DWORD_4b80_81a2 = 0;
loopCount = 40;
do {
EAX = DWORD_4b80_819a;
EBP = DWORD_4b80_819e;
if (EBP != 0) {
dividend2 = EAX;
divisor2 = EBP;
EAX = static_cast<int32_t>(dividend2 / divisor2);
EDX = dividend2 % divisor2;
if (EAX < 0) {
product2 = static_cast<int64_t>(EAX) * static_cast<int64_t>(DWORD_4b80_81aa);
EAX = static_cast<int32_t>(product2);
EDX = product2 >> 32;
EAX = (static_cast<uint32_t>(EAX) >> 31) | (EDX << 1);
}
EBX = EAX;
EBP = WORD_4b80_81ae | (WORD_4b80_81b0 << 16);
product2 = static_cast<int64_t>(EAX) * static_cast<int64_t>(EBP);
EAX = static_cast<int32_t>(product2);
EDX = product2 >> 32;
EAX = (static_cast<uint32_t>(EAX) >> 24) | (EDX << 8);
EAX += PlayerX + WORD_4b80_191b;
EAX >>= 6;
std::swap(EAX, EBX);
EBP = WORD_4b80_81b6 | (WORD_4b80_81b8 << 16);
product2 = static_cast<int64_t>(EAX) * static_cast<int64_t>(EBP);
EAX = static_cast<int32_t>(product2);
EDX = product2 >> 32;
EAX = (static_cast<uint32_t>(EAX) >> 24) | (EDX << 8);
EAX += PlayerZ + WORD_4b80_191d;
EAX >>= 6;
BX = static_cast<int16_t>(EBX);
BX &= 0x7F;
BX <<= 7;
AX = static_cast<int16_t>(EAX);
AX &= 0x7F;
BX += AX;
AX = FOGTXT[BX];
}
else {
AX = (EAX & 0x00FF) | 0x0C00;
}
// Write the value to the sample buffer FOGTXTSample, a short value array, at FOGTXTSampleIndex.
FOGTXTSample[FOGTXTSampleIndex] = AX; // Write the calculated value
FOGTXTSampleIndex++;
// Next is, in assembly:
// ADD dword[81a2], DWORD_VALUE1
// ADC dword[819e], DWORD_VALUE2
// ADD dword[81ae], DWORD_VALUE3
// ADD dword[81b2], DWORD_VALUE4
// ADD dword[81b6], DWORD_VALUE5
// The ADC instruction adds any carry from the preceding ADD instruction, so we need to get the carry
bool carry = false;
uint32_t before = DWORD_4b80_81a2;
DWORD_4b80_81a2 += DWORD_VALUE1;
if (DWORD_4b80_81a2 < before) {
carry = true; // overflow occurred
}
DWORD_4b80_819e += DWORD_VALUE2;
if (carry) {
DWORD_4b80_819e++;
}
int sum = (WORD_4b80_81ae & 0xFFFF | ((WORD_4b80_81b0 & 0xFFFF) << 16));
sum += DWORD_VALUE3;
WORD_4b80_81ae = static_cast<int16_t>(sum);
WORD_4b80_81b0 = sum >> 16;
sum = (WORD_4b80_81b2 & 0xFFFF | ((WORD_4b80_81b4 & 0xFFFF) << 16));
sum += DWORD_VALUE4;
WORD_4b80_81b2 = static_cast<int16_t>(sum);
WORD_4b80_81b4 = sum >> 16;
sum = (WORD_4b80_81b6 & 0xFFFF | ((WORD_4b80_81b8 & 0xFFFF) << 16));
sum += DWORD_VALUE5;
WORD_4b80_81b6 = static_cast<int16_t>(sum);
WORD_4b80_81b8 = sum >> 16;
loopCount--;
} while (loopCount != 0);
WORD_4b80_8208++;
} while (WORD_4b80_8208 != 25);
}
int16_t WORD_4b80_a76a = 0x533C;
int16_t WORD_4b80_a784 = 0x92; // Variable, but might always be 0x92 when fog functions called
int8_t FOGLGT[1663]; // The contents of FOG.LGT
int16_t ESArray[23360]; // For 320 columns x 146 rows of screen pixels. Already populated with the game screen when these functions are called.
// Depending on input values, BX may become out-of-range for accessing FOG.LGT. In this case the original game just uses the out-of-range data.
inline void ApplyNewData() {
BX += DX;
CX = static_cast<int16_t>((CX & 0xFF) | ((BX & 0xFF) << 8));
BX = (BX & 0xFF00) | (ESArray[DI / 2] & 0xFF);
AX = static_cast<int16_t>((AX & 0xFF00) | FOGLGT[BX]);
BX = (CX & 0xFF00) >> 8;
DX += BP;
BX += DX;
CX = static_cast<int16_t>((CX & 0xFF) | ((BX & 0xFF) << 8));
BX = (BX & 0xFF00) | (ESArray[DI / 2] & 0xFF00) >> 8;
AX = static_cast<int16_t>((AX & 0xFF) | (FOGLGT[BX] << 8));
ESArray[DI / 2] = AX;
DI += 2;
BX = (CX & 0xFF00) >> 8;
DX += BP;
}
inline void IterateOverData() {
DX = FOGTXTSample[SI / 2];
BP = FOGTXTSample[(SI + 2) / 2];
BP -= DX;
BP >>= 3;
FOGTXTSample[SI / 2] = DX + FOGTXTSample[(SI - (FOGTXTSample[41] - 80)) / 2];
ApplyNewData();
ApplyNewData();
ApplyNewData();
ApplyNewData();
SI++;
SI++;
}
static void ApplySampledFogData()
{
for (int32_t i = 0; i < 40; i++) {
FOGTXTSample[405 + i] = 0;
}
FOGTXTSample[43] = WORD_4b80_a76a;
FOGTXTSample[40] = (WORD_4b80_a784 + 7) >> 3;
FOGTXTSample[41] = 90;
FOGTXTSample[42] = 0;
do {
SI = FOGTXTSample[41] + 80;
FOGTXTSample[41] = SI;
for (int32_t i = 0; i < 40; i++) {
FOGTXTSample[0 + i] = (FOGTXTSample[(SI / 2) + i] - FOGTXTSample[(SI / 2) - 40 + i]) >> 3;
}
DI = FOGTXTSample[42];
ES = FOGTXTSample[43]; // 0x533C in testing, used for location of ESArray
CX = 8;
if (FOGTXTSample[40] == 1) {
CX -= 6;
}
do {
SI = FOGTXTSample[41] - 80;
BX = 0;
for (int32_t i = 0; i < 40; i++) {
IterateOverData();
}
CL = (CX & 0xFF);
CL--;
CX = static_cast<int16_t>((CX & 0xFF00) | CL);
} while (CL != 0);
FOGTXTSample[42] = DI;
FOGTXTSample[40]--;
} while (FOGTXTSample[40] != 0);
}Every game tick, if foggy weather is active, do:
WORD_4b80_191b += 4;
WORD_4b80_191d += 4;
SampleFOGTXT();
ApplySampledFogData();