~widelands-dev/widelands/multiplayer_dropdowns

« back to all changes in this revision

Viewing changes to src/wui/interactive_player.cc

Merged trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
58
58
using Widelands::Building;
59
59
using Widelands::Map;
60
60
 
 
61
namespace {
 
62
 
 
63
// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
 
64
// 'pf'. See 'field_brightness' in fields_to_draw.cc for scale of values.
 
65
float adjusted_field_brightness(const Widelands::FCoords& fcoords,
 
66
                                const uint32_t gametime,
 
67
                                const Widelands::Player::Field& pf) {
 
68
        if (pf.vision == 0) {
 
69
                return 0.;
 
70
        };
 
71
 
 
72
        uint32_t brightness = 144 + fcoords.field->get_brightness();
 
73
        brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
 
74
 
 
75
        if (pf.vision == 1) {
 
76
                static const uint32_t kDecayTimeInMs = 20000;
 
77
                const Widelands::Duration time_ago = gametime - pf.time_node_last_unseen;
 
78
                if (time_ago < kDecayTimeInMs) {
 
79
                        brightness = (brightness * (2 * kDecayTimeInMs - time_ago)) / (2 * kDecayTimeInMs);
 
80
                } else {
 
81
                        brightness = brightness / 2;
 
82
                }
 
83
        }
 
84
        return brightness / 255.;
 
85
}
 
86
 
 
87
void draw_immovables_for_visible_field(const Widelands::EditorGameBase& egbase,
 
88
                                       const FieldsToDraw::Field& field,
 
89
                                       const float scale,
 
90
                                       const TextToDraw text_to_draw,
 
91
                                       const Widelands::Player& player,
 
92
                                       RenderTarget* dst) {
 
93
        Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 
94
        if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
 
95
                TextToDraw draw_text_for_this_immovable = text_to_draw;
 
96
                const Widelands::Player* owner = imm->get_owner();
 
97
                if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
 
98
                        draw_text_for_this_immovable =
 
99
                           static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics);
 
100
                }
 
101
                imm->draw(
 
102
                   egbase.get_gametime(), draw_text_for_this_immovable, field.rendertarget_pixel, scale, dst);
 
103
        }
 
104
}
 
105
 
 
106
void draw_bobs_for_visible_field(const Widelands::EditorGameBase& egbase,
 
107
                                 const FieldsToDraw::Field& field,
 
108
                                 const float scale,
 
109
                                 const TextToDraw text_to_draw,
 
110
                                 const Widelands::Player& player,
 
111
                                 RenderTarget* dst) {
 
112
        for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
 
113
             bob = bob->get_next_bob()) {
 
114
                TextToDraw draw_text_for_this_bob = text_to_draw;
 
115
                const Widelands::Player* owner = bob->get_owner();
 
116
                if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
 
117
                        draw_text_for_this_bob =
 
118
                           static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics);
 
119
                }
 
120
                bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, scale, dst);
 
121
        }
 
122
}
 
123
 
 
124
void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field,
 
125
                                                const Widelands::Player::Field& player_field,
 
126
                                                const float scale,
 
127
                                                RenderTarget* dst) {
 
128
        if (const Widelands::MapObjectDescr* const map_object_descr =
 
129
               player_field.map_object_descr[Widelands::TCoords<>::None]) {
 
130
                if (player_field.constructionsite.becomes) {
 
131
                        assert(field.owner != nullptr);
 
132
                        const Widelands::ConstructionsiteInformation& csinf = player_field.constructionsite;
 
133
                        // draw the partly finished constructionsite
 
134
                        uint32_t anim_idx;
 
135
                        try {
 
136
                                anim_idx = csinf.becomes->get_animation("build");
 
137
                        } catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
 
138
                                try {
 
139
                                        anim_idx = csinf.becomes->get_animation("unoccupied");
 
140
                                } catch (Widelands::MapObjectDescr::AnimationNonexistent) {
 
141
                                        anim_idx = csinf.becomes->get_animation("idle");
 
142
                                }
 
143
                        }
 
144
                        const Animation& anim = g_gr->animations().get_animation(anim_idx);
 
145
                        const size_t nr_frames = anim.nr_frames();
 
146
                        uint32_t cur_frame =
 
147
                           csinf.totaltime ? csinf.completedtime * nr_frames / csinf.totaltime : 0;
 
148
                        uint32_t tanim = cur_frame * FRAME_LENGTH;
 
149
 
 
150
                        uint32_t percent = 100 * csinf.completedtime * nr_frames;
 
151
                        if (csinf.totaltime) {
 
152
                                percent /= csinf.totaltime;
 
153
                        }
 
154
                        percent -= 100 * cur_frame;
 
155
 
 
156
                        if (cur_frame) {  // not the first frame
 
157
                                // Draw the prev frame
 
158
                                dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim - FRAME_LENGTH,
 
159
                                                    field.owner->get_playercolor());
 
160
                        } else if (csinf.was) {
 
161
                                // Is the first frame, but there was another building here before,
 
162
                                // get its last build picture and draw it instead.
 
163
                                uint32_t a;
 
164
                                try {
 
165
                                        a = csinf.was->get_animation("unoccupied");
 
166
                                } catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
 
167
                                        a = csinf.was->get_animation("idle");
 
168
                                }
 
169
                                dst->blit_animation(field.rendertarget_pixel, scale, a, tanim - FRAME_LENGTH,
 
170
                                                    field.owner->get_playercolor());
 
171
                        }
 
172
                        dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim,
 
173
                                            field.owner->get_playercolor(), percent);
 
174
                } else if (upcast(const Widelands::BuildingDescr, building, map_object_descr)) {
 
175
                        assert(field.owner != nullptr);
 
176
                        // this is a building therefore we either draw unoccupied or idle animation
 
177
                        uint32_t pic;
 
178
                        try {
 
179
                                pic = building->get_animation("unoccupied");
 
180
                        } catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
 
181
                                pic = building->get_animation("idle");
 
182
                        }
 
183
                        dst->blit_animation(
 
184
                           field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor());
 
185
                } else if (map_object_descr->type() == Widelands::MapObjectType::FLAG) {
 
186
                        assert(field.owner != nullptr);
 
187
                        dst->blit_animation(field.rendertarget_pixel, scale, field.owner->tribe().flag_animation(),
 
188
                                            0, field.owner->get_playercolor());
 
189
                } else if (const uint32_t pic = map_object_descr->main_animation()) {
 
190
                        if (field.owner != nullptr) {
 
191
                                dst->blit_animation(
 
192
                                   field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor());
 
193
                        } else {
 
194
                                dst->blit_animation(field.rendertarget_pixel, scale, pic, 0);
 
195
                        }
 
196
                }
 
197
        }
 
198
}
 
199
 
 
200
}  // namespace
 
201
 
61
202
InteractivePlayer::InteractivePlayer(Widelands::Game& g,
62
203
                                     Section& global_s,
63
204
                                     Widelands::PlayerNumber const plyn,
114
255
        };
115
256
 
116
257
        set_player_number(plyn);
117
 
        map_view()->fieldclicked.connect(boost::bind(&InteractivePlayer::node_action, this));
 
258
        map_view()->field_clicked.connect([this](const Widelands::NodeAndTriangle<>& node_and_triangle) {
 
259
                node_action(node_and_triangle);
 
260
        });
118
261
 
119
262
        adjust_toolbar_position();
120
263
 
169
312
        }
170
313
}
171
314
 
 
315
void InteractivePlayer::draw(RenderTarget& dst) {
 
316
        // Bail out if the game isn't actually loaded.
 
317
        // This fixes a crash with displaying an error dialog during loading.
 
318
        if (!game().is_loaded())
 
319
                return;
 
320
 
 
321
        draw_map_view(map_view(), &dst);
 
322
}
 
323
 
 
324
void InteractivePlayer::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
 
325
        const Widelands::Player& plr = player();
 
326
        const auto& gbase = egbase();
 
327
        const Widelands::Map& map = gbase.map();
 
328
        const uint32_t gametime = gbase.get_gametime();
 
329
 
 
330
        auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
 
331
        const auto& roads_preview = road_building_preview();
 
332
 
 
333
        for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
 
334
                auto* f = fields_to_draw->mutable_field(idx);
 
335
 
 
336
                const Widelands::Player::Field& player_field =
 
337
                   plr.fields()[map.get_index(f->fcoords, map.get_width())];
 
338
 
 
339
                // Adjust this field for visibility for this player.
 
340
                if (!plr.see_all()) {
 
341
                        f->brightness = adjusted_field_brightness(f->fcoords, gametime, player_field);
 
342
                        f->roads = player_field.roads;
 
343
                        f->vision = player_field.vision;
 
344
                        if (player_field.vision == 0) {
 
345
                                // If the player cannot see the field, no need to do any more work.
 
346
                                continue;
 
347
                        } else if (player_field.vision == 1) {
 
348
                                f->owner = player_field.owner != 0 ? &gbase.player(player_field.owner) : nullptr;
 
349
                                f->is_border = player_field.border;
 
350
                        }
 
351
                }
 
352
 
 
353
                // Add road building overlays if applicable.
 
354
                const auto it = roads_preview.find(f->fcoords);
 
355
                if (it != roads_preview.end()) {
 
356
                        f->roads |= it->second;
 
357
                }
 
358
 
 
359
                const float scale = 1.f / given_map_view->view().zoom;
 
360
                draw_border_markers(*f, scale, *fields_to_draw, dst);
 
361
 
 
362
                // Render stuff that belongs to the node.
 
363
                if (f->vision > 1) {
 
364
                        const auto text_to_draw = get_text_to_draw();
 
365
                        draw_immovables_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
 
366
                        draw_bobs_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
 
367
                } else if (f->vision == 1) {
 
368
                        // We never show census or statistics for objects in the fog.
 
369
                        draw_immovables_for_formerly_visible_field(*f, player_field, scale, dst);
 
370
                }
 
371
 
 
372
                // TODO(sirver): Do not use the field_overlay_manager, instead draw the
 
373
                // overlays we are interested in here directly.
 
374
                field_overlay_manager().foreach_overlay(
 
375
                   f->fcoords, [dst, f, scale](const Image* pic, const Vector2i& hotspot) {
 
376
                           dst->blitrect_scale(Rectf(f->rendertarget_pixel - hotspot.cast<float>() * scale,
 
377
                                                     pic->width() * scale, pic->height() * scale),
 
378
                                               pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
 
379
                                               BlendMode::UseAlpha);
 
380
                        });
 
381
        }
 
382
}
 
383
 
172
384
void InteractivePlayer::popup_message(Widelands::MessageId const id,
173
385
                                      const Widelands::Message& message) {
174
386
        message_menu_.create();
191
403
}
192
404
 
193
405
/// Player has clicked on the given node; bring up the context menu.
194
 
void InteractivePlayer::node_action() {
 
406
void InteractivePlayer::node_action(const Widelands::NodeAndTriangle<>& node_and_triangle) {
195
407
        const Map& map = egbase().map();
196
 
        if (1 < player().vision(Map::get_index(get_sel_pos().node, map.get_width()))) {
 
408
        if (1 < player().vision(Map::get_index(node_and_triangle.node, map.get_width()))) {
197
409
                // Special case for buildings
198
 
                if (upcast(Building, building, map.get_immovable(get_sel_pos().node)))
 
410
                if (upcast(Building, building, map.get_immovable(node_and_triangle.node)))
199
411
                        if (can_see(building->owner().player_number())) {
200
 
                                show_building_window(get_sel_pos().node, false);
 
412
                                show_building_window(node_and_triangle.node, false);
201
413
                                return;
202
414
                        }
203
415