in coinrun/coinrun.cpp [1506:1673]
void paint_the_world(
QPainter& p, const QRect& rect,
const std::shared_ptr<State>& state, const Agent* agent,
bool recon, bool lasers)
{
const_cast<Agent*>(agent)->zoom = 0.9*agent->zoom + 0.1*agent->target_zoom;
double zoom = agent->zoom;
const double bgzoom = 0.4;
bool lowres = rect.height() < 200;
GroundTheme* ground_theme = choose_ground_theme(state->ground_n, lowres);
std::shared_ptr<Maze> maze = agent->maze;
bool maze_render = maze->game_type == CoinRunMaze_v0;
double kx = zoom * rect.width() / double(maze->h); // not w!
double ky = zoom * rect.height() / double(maze->h);
double dx = (-agent->x) * kx + rect.center().x() - 0.5*kx;
double dy = (agent->y) * ky - rect.center().y() - 0.5*ky;
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
for (int tile_x=-1; tile_x<=2; tile_x++) {
for (int tile_y=-1; tile_y<=1; tile_y++) {
double zx = rect.width()*zoom; // / bgzoom;
double zy = rect.height()*zoom; // / bgzoom);
QRectF bg_image = QRectF(0, 0, zx, zy);
bg_image.moveCenter(QPointF(
zx*tile_x + rect.center().x() + bgzoom*(dx + kx*maze->h/2),
zy*tile_y + rect.center().y() + bgzoom*(dy - ky*maze->h/2)
));
if (maze_render) {
p.fillRect(bg_image, QColor(30, 30, 30));
} else {
p.drawImage(bg_image, bg_images[state->bg_n]);
}
}
}
int radius = int(1 + maze->h / zoom); // actually /2 works except near scroll limits
int ix = int(agent->x + .5);
int iy = int(agent->y + .5);
int x_start = max(ix - radius, 0);
int x_end = min(ix + radius + 1, maze->w);
int y_start = max(iy - radius, 0);
int y_end = min(iy + radius + 1, maze->h);
double WINH = rect.height();
for (int y=y_start; y<y_end; ++y) {
for (int x=x_start; x<x_end; x++) {
int wkey = maze->get_elem(x, y);
if (wkey==SPACE) continue;
auto f = ground_theme->walls.find(wkey);
QImage img = f == ground_theme->walls.end() ? ground_theme->default_wall : f->second;
QRectF dst = QRectF(kx*x + dx, WINH - ky*y + dy, kx + .5, ky + .5);
dst.adjust(-0.1, -0.1, +0.1, +0.1); // here an attempt to fix subpixel seams that appear on the image, especially lowres
if (maze_render) {
if (is_coin(wkey)) {
p.fillRect(dst, QColor(255, 255, 0));
} else if (wkey == WALL_MIDDLE || wkey == WALL_SURFACE) {
p.fillRect(dst, QColor(150, 150, 150));
}
} else if (wkey==LAVA_MIDDLE || wkey==LAVA_SURFACE) {
QRectF d1 = dst;
QRectF d2 = dst;
QRectF sr(QPointF(0,0), img.size());
QRectF sr1 = sr;
QRectF sr2 = sr;
float tr = state->time*0.1;
tr -= int(tr);
tr *= -1;
d1.translate(tr*dst.width(), 0);
d2.translate(dst.width() + tr*dst.width(), 0);
sr1.translate(-tr*img.width(), 0);
sr2.translate(-img.width() - tr*img.width(), 0);
d1 &= dst;
d2 &= dst;
d1.adjust(0, 0, +0.5, 0);
d2.adjust(-0.5, 0, 0, 0);
sr1 &= sr;
sr2 &= sr;
if (!sr1.isEmpty())
p.drawImage(d1, img, sr1);
if (!sr2.isEmpty())
p.drawImage(d2, img, sr2);
} else {
p.drawImage(dst, img);
}
}
}
PlayerTheme* active_theme = choose_player_theme(agent->theme_n, agent->is_facing_right, lowres);
if (maze_render) {
QRectF dst = QRectF(kx * agent->x + dx, WINH - ky * (agent->y+1) + dy + ky, kx, ky);
p.fillRect(dst, QColor(0, 255, 199));
} else {
QImage img = agent->picture(active_theme);
QRectF dst = QRectF(kx * agent->x + dx, WINH - ky * (agent->y+1) + dy, kx, 2 * ky);
p.drawImage(dst, img);
}
int monsters_count = maze->monsters.size();
for (int i=0; i<monsters_count; ++i) {
const std::shared_ptr<Monster>& m = maze->monsters[i];
QRectF dst = QRectF(kx*m->x + dx, WINH - ky*m->y + dy, kx, ky);
EnemyTheme* theme = choose_enemy_theme(m, i, lowres);
if (m->is_flying || m->is_walking) {
for (int t=2; t<MONSTER_TRAIL; t+=2) {
QRectF dst = QRectF(kx*m->prev_x[t] + dx, WINH - ky*m->prev_y[t] + dy, kx, ky);
float ft = 1 - float(t)/MONSTER_TRAIL;
float smaller = 0.20;
float lower = -0.22;
float soar = -0.4;
dst.adjust(
(smaller-0.2*ft)*kx, (soar*ft-0.2*ft-lower+smaller)*ky,
(-smaller+0.2*ft)*kx, (soar*ft+0.2*ft-lower-smaller)*ky);
p.setBrush(QColor(255,255,255, t*127/MONSTER_TRAIL));
p.setPen(Qt::NoPen);
p.drawEllipse(dst);
}
}
p.drawImage(dst, state->time / theme->anim_freq % 2 == 0 ? theme->walk1 : theme->walk2);
}
if (USE_DATA_AUGMENTATION) {
float max_rand_dim = .25;
float min_rand_dim = .1;
int num_blotches = global_rand_gen.randint(0, 6);
bool hard_blotches = false;
if (hard_blotches) {
max_rand_dim = .3;
min_rand_dim = .2;
num_blotches = global_rand_gen.randint(0, 10);
}
for (int j = 0; j < num_blotches; j++) {
float rx = global_rand_gen.rand01() * rect.width();
float ry = global_rand_gen.rand01() * rect.height();
float rdx = (global_rand_gen.rand01() * max_rand_dim + min_rand_dim) * rect.width();
float rdy = (global_rand_gen.rand01() * max_rand_dim + min_rand_dim) * rect.height();
QRectF dst3 = QRectF(rx, ry, rdx, rdy);
p.fillRect(dst3, QColor(global_rand_gen.randint(0, 255), global_rand_gen.randint(0, 255), global_rand_gen.randint(0, 255)));
}
}
if (PAINT_VEL_INFO) {
float infodim = rect.height() * .2;
QRectF dst2 = QRectF(0, 0, infodim, infodim);
int s0 = to_shade(agent->spring / maze->max_jump);
int s1 = to_shade(.5 * agent->vx / maze->max_speed + .5);
int s2 = to_shade(.5 * agent->vy / maze->max_jump + .5);
p.fillRect(dst2, QColor(s1, s1, s1));
QRectF dst3 = QRectF(infodim, 0, infodim, infodim);
p.fillRect(dst3, QColor(s2, s2, s2));
}
}