File: util_weather.cpp

package info (click to toggle)
libtcod 1.24.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,728 kB
  • sloc: ansic: 46,186; cpp: 13,523; python: 4,814; makefile: 44; sh: 25
file content (257 lines) | stat: -rw-r--r-- 8,651 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 * Copyright (c) 2010 Jice
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * The name of Jice may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY JICE ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL JICE BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <math.h>

#include "main.hpp"

#define OCTAVES 7.0f
#define SCALE 2.0f
#define MAX_WIND_SPEED 4.0f
#define LIGHTNING_LEVEL 0.4f
#define LIGHTNING_RADIUS 500
#define LIGHTNING_LIFE 2.0f
#define LIGHTNING_INTENSITY_SPEED 20.0f
#define LIGHTNING_MIN_PROB 7.0f
#define LIGHTNING_MAX_PROB 1.0f
#define RAIN_MIN_PROB 4000
#define RAIN_MED_PROB 400
#define RAIN_MAX_PROB 10

void Weather::init(int width, int height) {
  map_ = new TCODHeightMap(width + 2, height + 2);
  // TODO : would be better with a 3d noise and slowly varying z
  // but you can notice the difference only when time is accelerated
  map_->addFbm(&noise2d, SCALE, SCALE, 0.0f, 0.0f, OCTAVES, 0.5f, 0.5f);
  dx_ = dy_ = noise_x_ = noise_y_ = 20000.0f;
  indicatorDelta_ = 0.0f;
  changeFactor_ = 1.0f;
  update(0.1f);
}

void Weather::move(int dx, int dy) {
  this->dx_ += dx;
  this->dy_ += dy;
}

const char* Weather::getWeather() {
  if (indicator_ > 0.9f)
    return "The sky is completely clear.";
  else if (indicator_ > 0.7f)
    return "The sky is clear.";
  else if (indicator_ > 0.6f)
    return "It's cloudy.";
  else if (indicator_ > 0.5f)
    return "You feel a breeze.";
  else if (indicator_ > 0.4f)
    return "It's drizzling.";
  else if (indicator_ > 0.3f)
    return "It's raining.";
  else if (indicator_ > 0.2f)
    return "You get caught in a storm.";
  else
    return "The storm is raging";
}

void Weather::update(float elapsed) {
  static float localElapsed = 0.0f;
  localElapsed += elapsed;
  float perlin_x = changeFactor_ * localElapsed / 100.0f;
  indicator_ = (1.0f + noise1d.get(&perlin_x, TCOD_NOISE_SIMPLEX)) * 0.5f + indicatorDelta_;
  indicator_ = CLAMP(0.0f, 1.0f, indicator_);
  float wind_speed = 1.0f - indicator_;
  perlin_x *= 2.0f;
  float windDir = (2.0f * 3.1415926f * 0.5f) * (1.0f + noise1d.get(&perlin_x, TCOD_NOISE_SIMPLEX));
  dx_ += MAX_WIND_SPEED * wind_speed * cosf(windDir) * elapsed;
  dy_ += 0.5f * MAX_WIND_SPEED * wind_speed * sinf(windDir) * elapsed;
  if (indicator_ < LIGHTNING_LEVEL) {
    float storm = (LIGHTNING_LEVEL - indicator_) / LIGHTNING_LEVEL;  // storm power 0-1
    float lp =
        LIGHTNING_MIN_PROB + (int)((LIGHTNING_MAX_PROB - LIGHTNING_MIN_PROB) * storm);  // nb of lightning per second
    int fps = TCODSystem::getFps();
    if (fps > 0) {
      int ilp = (int)(lp * fps);
      if (TCODRandom::getInstance()->getInt(0, ilp) == 0) {
        // new lightning
        lightning_t l;
        l.pos_x = TCODRandom::getInstance()->getInt(0, map_->w);
        l.pos_y = TCODRandom::getInstance()->getInt(0, map_->h);
        l.life = TCODRandom::getInstance()->getFloat(0.1f, LIGHTNING_LIFE);
        l.radius_squared = TCODRandom::getInstance()->getInt(LIGHTNING_RADIUS, LIGHTNING_RADIUS * 2);
        l.noise_x = TCODRandom::getInstance()->getFloat(0.0f, 1000.0f);
        l.intensity = 0.0f;
        lightnings_.push_back(l);
      }
    }
  }

  int bx = 0, by = 0;
  while (dx_ >= 1.0f) {
    dx_ -= 1.0f;
    noise_x_ += 1.0f;
    bx++;
  }
  while (dx_ <= -1.0f) {
    dx_ += 1.0f;
    noise_x_ -= 1.0f;
    bx--;
  }
  while (dy_ >= 1.0f) {
    dy_ -= 1.0f;
    noise_y_ += 1.0f;
    by++;
  }
  while (dy_ <= -1.0f) {
    dy_ += 1.0f;
    noise_y_ -= 1.0f;
    by--;
  }
  // update lightnings
  for (int i = static_cast<int>(lightnings_.size()) - 1; i >= 0; --i) {
    lightning_t& l = lightnings_.at(i);
    l.life -= elapsed;
    l.noise_x += elapsed * LIGHTNING_INTENSITY_SPEED;
    if (l.life <= 0) {
      lightnings_.erase(lightnings_.begin() + i);
      continue;
    } else {
      l.intensity = 0.5f * noise1d.get(&l.noise_x, TCOD_NOISE_SIMPLEX) + 1.0f;
      l.pos_x -= bx;
      l.pos_y -= by;
    }
  }

  if (bx || by) {
    // recompute the whole map
    // TODO : should recompute only missing rows/columns
    // the world generator demo has that, but only for
    // horizontal move. Here clouds move in any direction
    map_->clear();
    map_->addFbm(&noise2d, SCALE, SCALE, noise_x_, noise_y_, OCTAVES, 0.5f, 0.5f);
  }
}

float Weather::getCloud(int x, int y) {
  // cloud layer
  // 1.0 : no cloud
  // 0 : dark cloud. This way you can easily render ground with color * cloud coef
  float cdx = dx_, cdy = dy_;
  if (dx_ >= 0)
    x++;
  else
    cdx = dx_ + 1.0f;
  if (dy_ >= 0)
    y++;
  else
    cdy = dy_ + 1.0f;
  float val = map_->getInterpolatedValue(x + cdx, y + cdy);  // between 0 and 1
  val += 2 * indicator_ - 0.5f;
  val = CLAMP(0.2f, 1.0f, val);
  return val;
}

float Weather::getLightning(int x, int y) {
  if (indicator_ >= 0.3f) return 0.0f;
  if (dx_ >= 0) x++;
  if (dy_ >= 0) y++;
  float res = 0.0f;
  float cloud = map_->getValue(x, y);
  cloud = 1.0f - cloud;  // inverted cloud. 0 = sky, 1=dark cloud
  cloud -= 0.6f;  // no lightning under 0.6f. cloud is now 0 - 0.4
  if (cloud <= 0.0f) return 0.0f;
  cloud = cloud / 0.4f;  // now back to 0-1 range (but only for really cloudy zones)
  for (const lightning_t& l : lightnings_) {
    int dx = l.pos_x - x;
    int dy = l.pos_y - y;
    int dist = dx * dx + dy * dy;
    if (dist < l.radius_squared) {
      res += l.intensity * (float)(l.radius_squared - dist) / l.radius_squared;
    }
  }
  float ret = cloud * res;
  return CLAMP(0.0f, 1.0f, ret);
}

bool Weather::hasRainDrop() {
  if (indicator_ >= 0.5f) return false;
  int prob;
  if (indicator_ >= 0.3f) {
    prob = (int)(RAIN_MIN_PROB + (RAIN_MED_PROB - RAIN_MIN_PROB) * (0.5f - indicator_) * 5);
  } else {
    prob = (int)(RAIN_MED_PROB + (RAIN_MAX_PROB - RAIN_MED_PROB) * (0.3f - indicator_) * 3.33f);
  }
  int rp = TCODRandom::getInstance()->getInt(0, prob);
  return rp == 0;
}

void Weather::calculateAmbient(float timeInSeconds) {
  // calculate ambient light
  static TCODColor night(0, 0, 128);
  static TCODColor dawn(196, 0, 0);
  static TCODColor dawn2(220, 200, 64);
  static TCODColor day(255, 255, 195);
  float coef = 0.0f;
  float hour = timeInSeconds / 3600.0f;
  // TODO : should use a color gradient map for that..
  if (hour > 21.0f || hour < 6.0f) {
    ambientColor_ = night;  // night
    coef = 0.0;
  } else if (hour < 7.0f) {
    // between 6am and 7am
    coef = (hour - 6.0f);
    ambientColor_ = TCODColor::lerp(night, dawn, coef);
    coef /= 3.0f;
  } else if (hour < 8.0f) {
    // between 7am and 8am
    coef = (hour - 7.0f);
    ambientColor_ = TCODColor::lerp(dawn, dawn2, coef);
    coef = 0.33333f + coef / 3.0f;
  } else if (hour < 9.0f) {
    // between 8am and 9am
    coef = (hour - 8.0f);
    ambientColor_ = TCODColor::lerp(dawn2, day, coef);
    coef = 0.66666f + coef / 3.0f;
  } else if (hour < 18.0f) {
    // between 9am and 6pm
    ambientColor_ = day;
    coef = 1.0f;
  } else if (hour < 19.0f) {
    // between 6pm and 7pm
    coef = (hour - 18.0f);
    ambientColor_ = TCODColor::lerp(day, dawn2, coef);
    coef = 0.66666f + (1.0f - coef) / 3.0f;
  } else if (hour < 20.0f) {
    // between 7pm and 8pm
    coef = (hour - 19.0f);
    ambientColor_ = TCODColor::lerp(dawn2, dawn, coef);
    coef = 0.33333f + (1.0f - coef) / 3.0f;
  } else if (hour < 21.0f) {
    // between 8pm and 9pm
    coef = (hour - 20.0f);
    ambientColor_ = TCODColor::lerp(dawn, night, coef);
    coef = (1.0f - coef) / 3.0f;
  }
}