1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
46
47 #include "gui/MiniMap.h"
48
49 #include <cstdio>
50
51 #include "core/Core.h"
52 #include "core/Localisation.h"
53
54 #include "game/EntityManager.h"
55 #include "game/Levels.h"
56 #include "game/NPC.h"
57 #include "game/Player.h"
58
59 #include "gui/Text.h"
60 #include "gui/Interface.h"
61
62 #include "graphics/Draw.h"
63 #include "graphics/Math.h"
64 #include "graphics/data/TextureContainer.h"
65 #include "graphics/texture/TextureStage.h"
66 #include "graphics/texture/Texture.h"
67
68 #include "io/log/Logger.h"
69
70 #include "scene/Interactive.h"
71 #include "scene/SaveFormat.h"
72
73 MiniMap g_miniMap; // TODO: remove this
74
getData(int showLevel)75 void MiniMap::getData(int showLevel) {
76
77 if(m_levels[showLevel].m_texContainer == NULL) {
78
79 char name[256];
80 char levelMap[256];
81 GetLevelNameByNum(showLevel, name);
82
83 sprintf(levelMap, "graph/levels/level%s/map", name);
84 m_levels[showLevel].m_texContainer = TextureContainer::Load(levelMap);
85
86 if(m_levels[showLevel].m_texContainer) { // 4 pix/meter
87
88 m_levels[showLevel].m_height = static_cast<float>(m_levels[showLevel].m_texContainer->m_dwHeight);
89 m_levels[showLevel].m_width = static_cast<float>(m_levels[showLevel].m_texContainer->m_dwWidth);
90
91 float minX = std::numeric_limits<float>::max();
92 float maxX = std::numeric_limits<float>::min();
93 float minY = std::numeric_limits<float>::max();
94 float maxY = std::numeric_limits<float>::min();
95
96 EERIEPOLY *ep = NULL;
97 EERIE_BKG_INFO *eg = NULL;
98
99 for(int j = 0; j < m_activeBkg->Zsize; j++) {
100
101 for(int i = 0; i < m_activeBkg->Xsize; i++) {
102 eg = &m_activeBkg->Backg[i+j*m_activeBkg->Xsize];
103 for(int k = 0; k < eg->nbpoly; k++) {
104 ep = &eg->polydata[k];
105 if(ep) {
106 minX = min(minX, ep->min.x);
107 maxX = max(maxX, ep->max.x);
108 minY = min(minY, ep->min.z);
109 maxY = max(maxY, ep->max.z);
110 }
111 }
112 }
113
114 m_mapMaxY[showLevel] = maxY;
115 m_levels[showLevel].m_ratioX = minX;
116 m_levels[showLevel].m_ratioY = minY;
117
118 for(int l = 0; l < MAX_MINIMAP_LEVELS; l++) {
119 m_levels[l].m_offsetX = 0;
120 m_levels[l].m_offsetY = 0;
121 }
122 }
123 }
124 }
125 }
126
validatePos()127 void MiniMap::validatePos() {
128
129 int showLevel = ARX_LEVELS_GetRealNum(m_currentLevel);
130
131 if((showLevel >= 0) && (showLevel < MAX_MINIMAP_LEVELS)) {
132
133 if(m_levels[showLevel].m_texContainer == NULL) {
134 getData(showLevel);
135 }
136
137 if(m_levels[m_currentLevel].m_texContainer == NULL) {
138 getData(m_currentLevel);
139 }
140
141 if(m_levels[showLevel].m_texContainer) {
142 revealPlayerPos(ARX_LEVELS_GetRealNum(m_currentLevel));
143 }
144 }
145 }
146
validatePlayerPos(int currentLevel,long blockPlayerControls,ARX_INTERFACE_BOOK_MODE bookMode)147 void MiniMap::validatePlayerPos(int currentLevel, long blockPlayerControls, ARX_INTERFACE_BOOK_MODE bookMode) {
148
149 m_currentLevel = currentLevel;
150
151 if(!blockPlayerControls) {
152
153 float req;
154
155 if((m_player->Interface & INTER_MAP) && (!(m_player->Interface & INTER_COMBATMODE)) && (bookMode == BOOKMODE_MINIMAP)) {
156 req = 20.f;
157 } else {
158 req = 80.f;
159 }
160
161 if(fartherThan(Vec2f(m_playerLastPosX, m_playerLastPosZ), Vec2f(m_player->pos.x, m_player->pos.z), req)) {
162 m_playerLastPosX = m_player->pos.x;
163 m_playerLastPosZ = m_player->pos.z;
164 validatePos();
165 }
166 }
167 }
168
loadOffsets(PakReader * pakRes)169 void MiniMap::loadOffsets(PakReader *pakRes) {
170
171 std::string iniMiniOffsets = "graph/levels/mini_offsets.ini";
172
173 PakFile *file = pakRes->getFile(iniMiniOffsets.c_str());
174
175 if(!file) {
176 LogError << "Missing " << iniMiniOffsets;
177 return;
178 }
179
180 size_t fileSize = file->size();
181 char *dat = new char[fileSize + 2];
182 dat[fileSize + 1] = '\0';
183
184 file->read(dat);
185
186 if(dat) {
187
188 size_t pos = 0;
189
190 for(int i = 0; i < 29; i++) { // Why 29?
191
192 char t[512];
193 int nRead = sscanf(dat + pos, "%s %f %f", t, &m_miniOffsetX[i], &m_miniOffsetY[i]);
194
195 if(nRead != 3) {
196 LogError << "Error parsing line " << i << " of mini_offsets.ini: read " << nRead;
197 }
198
199 while((pos < fileSize) && (dat[pos] != '\n')) {
200 pos++;
201 }
202
203 pos++;
204
205 if(pos >= fileSize) {
206 break;
207 }
208 }
209
210 delete[] dat;
211 }
212
213 m_miniOffsetX[0] = 0;
214 m_miniOffsetY[0] = -0.5;
215 m_miniOffsetX[1] = 0;
216 m_miniOffsetY[1] = 0;
217 m_miniOffsetX[14] = 130;
218 m_miniOffsetY[14] = 0;
219 m_miniOffsetX[15] = 31;
220 m_miniOffsetY[15] = -3.5;
221 }
222
reveal()223 void MiniMap::reveal() {
224
225 for(int d = 0; d < MAX_MINIMAP_LEVELS; d++) {
226 for(int j = 0; j < MINIMAP_MAX_Z; j++) {
227 for(int i = 0; i < MINIMAP_MAX_X; i++) {
228 m_levels[d].m_revealed[i][j] = 255;
229 }
230 }
231 }
232 }
233
firstInit(ARXCHARACTER * pl,PakReader * pakRes,EntityManager * entityMng)234 void MiniMap::firstInit(ARXCHARACTER *pl, PakReader *pakRes, EntityManager *entityMng) {
235
236 m_pTexDetect = NULL;
237 m_mapMarkerTexCont = NULL;
238
239 m_player = pl;
240 m_playerLastPosX = -999999.f;
241 m_playerLastPosZ = -999999.f;
242
243 m_modX = (float)MAX_BKGX / (float)MINIMAP_MAX_X;
244 m_modZ = (float)MAX_BKGZ / (float)MINIMAP_MAX_Z;
245
246 m_currentLevel = 0;
247 m_entities = entityMng;
248 m_activeBkg = NULL;
249
250 resetLevels();
251
252 for(int i = 0; i < MAX_MINIMAP_LEVELS; i++) {
253 m_miniOffsetX[i] = 0;
254 m_miniOffsetY[i] = 0;
255 }
256
257 loadOffsets(pakRes);
258 }
259
resetLevels()260 void MiniMap::resetLevels() {
261
262 for(int i = 0; i < MAX_MINIMAP_LEVELS; i++) {
263 m_levels[i].m_texContainer = NULL;
264 m_levels[i].m_offsetX = 0.f;
265 m_levels[i].m_offsetY = 0.f;
266 m_levels[i].m_ratioX = 0.f;
267 m_levels[i].m_ratioY = 0.f;
268 m_levels[i].m_width = 0.f;
269 m_levels[i].m_height = 0.f;
270 memset(m_levels[i].m_revealed, 0, sizeof(m_levels[i].m_revealed[0][0] * MINIMAP_MAX_X * MINIMAP_MAX_Z)); // Sets the whole array to 0
271 }
272 }
273
reset()274 void MiniMap::reset() {
275
276 purgeTexContainer();
277 resetLevels();
278 }
279
purgeTexContainer()280 void MiniMap::purgeTexContainer() {
281
282 for(int i = 0; i < MAX_MINIMAP_LEVELS; i++) {
283 delete m_levels[i].m_texContainer;
284 m_levels[i].m_texContainer = NULL;
285 }
286 }
287
showPlayerMiniMap(int showLevel)288 void MiniMap::showPlayerMiniMap(int showLevel) {
289
290 const float miniMapZoom = 300.f; // zoom of the minimap
291 const Rect miniMapRect(390, 135, 590, 295); // minimap rect on a 640*480 screen
292 const float playerSize = 4.f; // red arrow size
293
294 const float decalY = -150;
295 const float decalX = +40;
296
297 // First Load Minimap TC & DATA if needed
298 if(m_levels[showLevel].m_texContainer == NULL) {
299 getData(showLevel);
300 }
301
302 if(m_levels[showLevel].m_texContainer) {
303
304 GRenderer->SetRenderState(Renderer::DepthTest, false);
305
306 float startX = 0.f;
307 float startY = 0.f;
308
309 Vec2f playerPos(0.f, 0.f);
310
311 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
312 playerPos = computePlayerPos(miniMapZoom, showLevel);
313 startX = 490.f - playerPos.x;
314 startY = 220.f - playerPos.y;
315 playerPos.x += startX;
316 playerPos.y += startY;
317 }
318
319 // Draw the background
320 drawBackground(showLevel, Rect(390, 135, 590, 295), startX, startY, miniMapZoom, 20.f, decalX, decalY, true, 0.5f);
321
322 GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapRepeat);
323
324 // Draw the player (red arrow)
325 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
326 drawPlayer(playerSize, playerPos.x + decalX, playerPos.y + decalY, true);
327 drawDetectedEntities(showLevel, startX + decalX, startY + decalY, miniMapZoom);
328 }
329
330 }
331 }
332
showBookMiniMap(int showLevel)333 void MiniMap::showBookMiniMap(int showLevel) {
334
335 // First Load Minimap TC & DATA if needed
336 if(m_levels[showLevel].m_texContainer == NULL) {
337 getData(showLevel);
338 }
339
340 if(m_levels[showLevel].m_texContainer) {
341
342 GRenderer->SetRenderState(Renderer::DepthTest, false);
343
344 float zoom = 900.f;
345 float startX = 0.f;
346 float startY = 0.f;
347
348 Vec2f playerPos(0.f, 0.f);
349
350 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
351 playerPos = computePlayerPos(zoom, showLevel);
352 startX = 490.f - playerPos.x;
353 startY = 220.f - playerPos.y;
354 playerPos.x += startX;
355 playerPos.y += startY;
356 }
357
358 drawBackground(showLevel, Rect(360, 85, 555, 355), startX, startY, zoom, 20.f);
359
360 GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapRepeat);
361
362 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
363 drawPlayer(6.f, playerPos.x, playerPos.y);
364 drawDetectedEntities(showLevel, startX, startY, zoom);
365 }
366
367 }
368 }
369
showBookEntireMap(int showLevel)370 void MiniMap::showBookEntireMap(int showLevel) {
371
372 // First Load Minimap TC & DATA if needed
373 if(m_levels[showLevel].m_texContainer == NULL) {
374 getData(showLevel);
375 }
376
377 if(!m_levels[showLevel].m_texContainer) {
378 return;
379 }
380
381 GRenderer->SetRenderState(Renderer::DepthTest, false);
382
383 float zoom = 250.f;
384 float startX = 140.f;
385 float startY = 120.f;
386
387 Vec2f playerPos(0.f, 0.f);
388
389 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
390 playerPos = computePlayerPos(zoom, showLevel);
391 playerPos.x += startX;
392 playerPos.y += startY;
393 }
394
395 drawBackground(showLevel, Rect(0, 0, 345, 290), startX, startY, zoom);
396
397 GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapRepeat);
398
399 if(showLevel == ARX_LEVELS_GetRealNum(m_currentLevel)) {
400 drawPlayer(3.f, playerPos.x, playerPos.y);
401 drawDetectedEntities(showLevel, startX, startY, zoom);
402 }
403
404 TexturedVertex verts[4];
405 for(int k = 0; k < 4; k++) {
406 verts[k].color = 0xFFFFFFFF;
407 verts[k].rhw = 1;
408 verts[k].p.z = 0.00001f;
409 }
410
411 float caseX = zoom / ((float)MINIMAP_MAX_X);
412 float caseY = zoom / ((float)MINIMAP_MAX_Z);
413 float ratio = 1.f;
414
415 for(size_t i = 0; i < m_mapMarkers.size(); i++) {
416
417 if(m_mapMarkers[i].m_lvl != showLevel + 1) {
418 continue;
419 }
420
421 float pos_x = m_mapMarkers[i].m_x * 8 * ratio * m_activeBkg->Xmul * caseX + startX;
422 float pos_y = m_mapMarkers[i].m_y * 8 * ratio * m_activeBkg->Zmul * caseY + startY;
423 float size = 5.f * ratio;
424 verts[0].color = 0xFFFF0000;
425 verts[1].color = 0xFFFF0000;
426 verts[2].color = 0xFFFF0000;
427 verts[3].color = 0xFFFF0000;
428 verts[0].p.x = (pos_x - size) * Xratio;
429 verts[0].p.y = (pos_y - size) * Yratio;
430 verts[1].p.x = (pos_x + size) * Xratio;
431 verts[1].p.y = (pos_y - size) * Yratio;
432 verts[2].p.x = (pos_x + size) * Xratio;
433 verts[2].p.y = (pos_y + size) * Yratio;
434 verts[3].p.x = (pos_x - size) * Xratio;
435 verts[3].p.y = (pos_y + size) * Yratio;
436 verts[0].uv = Vec2f::ZERO;
437 verts[1].uv = Vec2f::X_AXIS;
438 verts[2].uv = Vec2f::ONE;
439 verts[3].uv = Vec2f::Y_AXIS;
440
441 if(MouseInRect(verts[0].p.x, verts[0].p.y, verts[2].p.x, verts[2].p.y)) {
442 if(!m_mapMarkers[i].m_text.empty()) {
443
444 Rect bRect(140, 290, 140 + 205, 358);
445
446 Rect::Num left = checked_range_cast<Rect::Num>((bRect.left) * Xratio);
447 Rect::Num right = checked_range_cast<Rect::Num>((bRect.right) * Xratio);
448 Rect::Num top = checked_range_cast<Rect::Num>((bRect.top) * Yratio);
449 Rect::Num bottom = checked_range_cast<Rect::Num>((bRect.bottom) * Yratio);
450 Rect rRect = Rect(left, top, right, bottom);
451
452 long lLengthDraw = ARX_UNICODE_ForceFormattingInRect(hFontInGameNote, m_mapMarkers[i].m_text, rRect);
453
454 DrawBookTextInRect(hFontInGameNote, float(bRect.left), float(bRect.top), float(bRect.right), m_mapMarkers[i].m_text.substr(0, lLengthDraw), Color::none);
455 }
456 }
457
458 if(m_mapMarkerTexCont == NULL) {
459 m_mapMarkerTexCont = TextureContainer::Load("graph/interface/icons/mapmarker");
460 }
461
462 GRenderer->SetTexture(0, m_mapMarkerTexCont);
463
464 EERIEDRAWPRIM(Renderer::TriangleFan, verts, 4);
465 }
466 }
467
revealPlayerPos(int showLevel)468 void MiniMap::revealPlayerPos(int showLevel) {
469
470 float zoom = 250.f;
471 float startX = 140.f;
472 float startY = 120.f;
473 float caseX = zoom / ((float)MINIMAP_MAX_X);
474 float caseY = zoom / ((float)MINIMAP_MAX_Z);
475
476 Vec2f playerPos = computePlayerPos(zoom, showLevel);
477 playerPos.x += startX;
478 playerPos.y += startY;
479
480 // TODO this is inefficient - we don't really need to iterate over the whole minimap!
481 // only the area around the player will be modified
482 for(int j = 0; j < MINIMAP_MAX_Z; j++) {
483 for(int i = 0; i < MINIMAP_MAX_X; i++) {
484
485 float posx = startX + i * caseX;
486 float posy = startY + j * caseY;
487
488 float d = fdist(Vec2f(posx + caseX * 0.5f, posy), playerPos);
489 if(d > 6.f) {
490 continue;
491 }
492
493 float vv = (6 - d) * (1.f / 6);
494
495 if(vv >= 0.5f) {
496 vv = 1.f;
497 } else if(vv > 0.f) {
498 vv = vv * 2.f;
499 } else {
500 vv = 0.f;
501 }
502
503 int r = vv * 255.f;
504
505 int ucLevel = max(r, (int)m_levels[showLevel].m_revealed[i][j]);
506 m_levels[showLevel].m_revealed[i][j] = checked_range_cast<unsigned char>(ucLevel);
507 }
508 }
509 }
510
computePlayerPos(float zoom,int showLevel)511 Vec2f MiniMap::computePlayerPos(float zoom, int showLevel) {
512
513 float caseX = zoom / ((float)MINIMAP_MAX_X);
514 float caseY = zoom / ((float)MINIMAP_MAX_Z);
515 float ratio = zoom / 250.f;
516
517 Vec2f pos(0.f, 0.f);
518
519 float ofx = m_miniOffsetX[m_currentLevel];
520 float ofx2 = m_levels[showLevel].m_ratioX;
521 float ofy = m_miniOffsetY[m_currentLevel];
522 float ofy2 = m_levels[showLevel].m_ratioY;
523
524 pos.x = ((m_player->pos.x + ofx - ofx2) * ( 1.0f / 100 ) * caseX
525 + m_miniOffsetX[m_currentLevel] * ratio * m_modX) / m_modX;
526 pos.y = ((m_mapMaxY[showLevel] - ofy - ofy2) * ( 1.0f / 100 ) * caseY
527 - (m_player->pos.z + ofy - ofy2) * ( 1.0f / 100 ) * caseY + m_miniOffsetY[m_currentLevel] * ratio * m_modZ) / m_modZ;
528
529 return pos;
530 }
531
drawBackground(int showLevel,Rect boundaries,float startX,float startY,float zoom,float fadeBorder,float decalX,float decalY,bool invColor,float alpha)532 void MiniMap::drawBackground(int showLevel, Rect boundaries, float startX, float startY, float zoom, float fadeBorder, float decalX, float decalY, bool invColor, float alpha) {
533
534 float caseX = zoom / ((float)MINIMAP_MAX_X);
535 float caseY = zoom / ((float)MINIMAP_MAX_Z);
536
537 TexturedVertex verts[4];
538 GRenderer->SetTexture(0, m_levels[showLevel].m_texContainer);
539
540 for(int k = 0; k < 4; k++) {
541 verts[k].color = 0xFFFFFFFF;
542 verts[k].rhw = 1;
543 verts[k].p.z = 0.00001f;
544 }
545
546 float div = (1.0f / 25);
547 TextureContainer * tc = m_levels[showLevel].m_texContainer;
548 float dw = 1.f / tc->m_pTexture->getStoredSize().x;
549 float dh = 1.f / tc->m_pTexture->getStoredSize().y;
550
551 float vx2 = 4.f * dw * m_modX;
552 float vy2 = 4.f * dh * m_modZ;
553
554 float fadeDiv = 0.f;
555 Rect fadeBounds(0, 0, 0, 0);
556
557 if(fadeBorder > 0.f) {
558 fadeDiv = 1.f/fadeBorder;
559 fadeBounds.left = checked_range_cast<Rect::Num>((boundaries.left + fadeBorder) * Xratio);
560 fadeBounds.right = checked_range_cast<Rect::Num>((boundaries.right - fadeBorder) * Xratio);
561 fadeBounds.top = checked_range_cast<Rect::Num>((boundaries.top + fadeBorder) * Yratio);
562 fadeBounds.bottom = checked_range_cast<Rect::Num>((boundaries.bottom - fadeBorder) * Yratio);
563 }
564
565 GRenderer->SetRenderState(Renderer::AlphaBlending, true);
566 if(invColor) {
567 GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendInvSrcColor);
568 } else {
569 GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
570 }
571 GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapClamp);
572
573 for(int j = -2; j < MINIMAP_MAX_Z + 2; j++) {
574 for(int i = -2; i < MINIMAP_MAX_X + 2; i++) {
575
576 float vx, vy, vxx, vyy;
577 vxx = ((float)i * (float)m_activeBkg->Xdiv * m_modX);
578 vyy = ((float)j * (float)m_activeBkg->Zdiv * m_modZ);
579 vx = (vxx * div) * dw;
580 vy = (vyy * div) * dh;
581
582 float posx = (startX + i * caseX) * Xratio;
583 float posy = (startY + j * caseY) * Yratio;
584
585 if((posx < boundaries.left * Xratio)
586 || (posx > boundaries.right * Xratio)
587 || (posy < boundaries.top * Yratio)
588 || (posy > boundaries.bottom * Yratio)) {
589 continue; // out of bounds
590 }
591
592 verts[3].p.x = verts[0].p.x = (posx);
593 verts[1].p.y = verts[0].p.y = (posy);
594 verts[2].p.x = verts[1].p.x = posx + (caseX * Xratio);
595 verts[3].p.y = verts[2].p.y = posy + (caseY * Yratio);
596
597 verts[3].uv.x = verts[0].uv.x = vx;
598 verts[1].uv.y = verts[0].uv.y = vy;
599 verts[2].uv.x = verts[1].uv.x = vx + vx2;
600 verts[3].uv.y = verts[2].uv.y = vy + vy2;
601
602 float v;
603 float oo = 0.f;
604
605 for(int vert = 0; vert < 4; vert++) {
606
607 // Array offset according to "vert"
608 int iOffset = 0;
609 int jOffset = 0;
610
611 if(vert == 1 || vert == 2)
612 iOffset = 1;
613 if(vert == 2 || vert == 3)
614 jOffset = 1;
615
616 if((i + iOffset < 0) || (i + iOffset >= MINIMAP_MAX_X) || (j + jOffset < 0) || (j + jOffset >= MINIMAP_MAX_Z)) {
617 v = 0;
618 } else {
619 v = ((float)m_levels[showLevel].m_revealed[min(i+iOffset, MINIMAP_MAX_X-iOffset)][min(j+jOffset, MINIMAP_MAX_Z-jOffset)]) * (1.0f / 255);
620 }
621
622 if(fadeBorder > 0.f) {
623
624 float _px = verts[vert].p.x - fadeBounds.left;
625
626 if(_px < 0.f) {
627 v = 0.f;
628 } else if(_px < fadeBorder) {
629 v *= _px * fadeDiv;
630 }
631
632 _px = fadeBounds.right - verts[vert].p.x;
633
634 if(_px < 0.f) {
635 v = 0.f;
636 } else if(_px < fadeBorder) {
637 v *= _px * fadeDiv;
638 }
639
640 _px = verts[vert].p.y - fadeBounds.top;
641
642 if(_px < 0.f) {
643 v = 0.f;
644 } else if(_px < fadeBorder) {
645 v *= _px * fadeDiv;
646 }
647
648 _px = fadeBounds.bottom - verts[vert].p.y;
649
650 if(_px < 0.f) {
651 v = 0.f;
652 } else if(_px < fadeBorder) {
653 v *= _px * fadeDiv;
654 }
655 }
656
657 verts[vert].color = Color::gray(v * alpha).toBGR();
658
659 oo += v;
660 }
661
662 if(oo > 0.f) {
663
664 verts[0].p.x += decalX * Xratio;
665 verts[0].p.y += decalY * Yratio;
666 verts[1].p.x += decalX * Xratio;
667 verts[1].p.y += decalY * Yratio;
668 verts[2].p.x += decalX * Xratio;
669 verts[2].p.y += decalY * Yratio;
670 verts[3].p.x += decalX * Xratio;
671 verts[3].p.y += decalY * Yratio;
672
673 EERIEDRAWPRIM(Renderer::TriangleFan, verts, 4);
674 }
675 }
676 }
677
678 GRenderer->SetRenderState(Renderer::AlphaBlending, false);
679 }
680
drawPlayer(float playerSize,float playerX,float playerY,bool alphaBlending)681 void MiniMap::drawPlayer(float playerSize, float playerX, float playerY, bool alphaBlending) {
682
683 TexturedVertex verts[4];
684
685 for(int k = 0; k < 4; k++) {
686 verts[k].color = 0xFFFF0000; // red
687 verts[k].rhw = 1;
688 verts[k].p.z = 0.00001f;
689 }
690
691 float rx = 0.f;
692 float ry = -playerSize * 1.8f;
693 float rx2 = -playerSize * (1.0f / 2);
694 float ry2 = playerSize;
695 float rx3 = playerSize * (1.0f / 2);
696 float ry3 = playerSize;
697
698 float angle = radians(m_player->angle.b);
699 float ca = EEcos(angle);
700 float sa = EEsin(angle);
701
702 verts[0].p.x = (playerX + rx2 * ca + ry2 * sa) * Xratio;
703 verts[0].p.y = (playerY + ry2 * ca - rx2 * sa) * Yratio;
704 verts[1].p.x = (playerX + rx * ca + ry * sa) * Xratio;
705 verts[1].p.y = (playerY + ry * ca - rx * sa) * Yratio;
706 verts[2].p.x = (playerX + rx3 * ca + ry3 * sa) * Xratio;
707 verts[2].p.y = (playerY + ry3 * ca - rx3 * sa) * Yratio;
708
709 GRenderer->ResetTexture(0);
710 GRenderer->SetRenderState(Renderer::AlphaBlending, alphaBlending);
711 if(alphaBlending) {
712 GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendInvSrcColor);
713 }
714
715 EERIEDRAWPRIM(Renderer::TriangleFan, verts);
716
717 GRenderer->SetRenderState(Renderer::AlphaBlending, false);
718 }
719
drawDetectedEntities(int showLevel,float startX,float startY,float zoom)720 void MiniMap::drawDetectedEntities(int showLevel, float startX, float startY, float zoom) {
721
722 float caseX = zoom / ((float)MINIMAP_MAX_X);
723 float caseY = zoom / ((float)MINIMAP_MAX_Z);
724 float ratio = zoom / 250.f;
725
726 if(!m_pTexDetect) {
727 m_pTexDetect = TextureContainer::Load("graph/particles/flare");
728 }
729
730 // Computes playerpos
731 float ofx = m_miniOffsetX[m_currentLevel];
732 float ofx2 = m_levels[showLevel].m_ratioX;
733 float ofy = m_miniOffsetY[m_currentLevel];
734 float ofy2 = m_levels[showLevel].m_ratioY;
735
736 GRenderer->SetRenderState(Renderer::AlphaBlending, true);
737 GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
738
739 const EntityManager &ents = *m_entities; // for convenience
740 for(size_t lnpc = 1; lnpc < ents.size(); lnpc++) {
741 Entity * npc = ents[lnpc];
742
743 if(!npc || !(npc->ioflags & IO_NPC)) {
744 continue; // only NPCs can be detected
745 }
746
747 if(npc->_npcdata->life < 0.f) {
748 continue; // don't show dead NPCs
749 }
750
751 if((npc->gameFlags & GFLAG_MEGAHIDE) || npc->show != SHOW_FLAG_IN_SCENE) {
752 continue; // don't show hidden NPCs
753 }
754
755 if(npc->_npcdata->fDetect < 0) {
756 continue; // don't show undetectable NPCs
757 }
758
759 if(player.Full_Skill_Etheral_Link < npc->_npcdata->fDetect) {
760 continue; // the player doesn't have enough skill to detect this NPC
761 }
762
763 float fpx = startX + ((npc->pos.x - 100 + ofx - ofx2) * ( 1.0f / 100 ) * caseX
764 + m_miniOffsetX[m_currentLevel] * ratio * m_modX) / m_modX;
765 float fpy = startY + ((m_mapMaxY[showLevel] - ofy - ofy2) * ( 1.0f / 100 ) * caseY
766 - (npc->pos.z + 200 + ofy - ofy2) * ( 1.0f / 100 ) * caseY + m_miniOffsetY[m_currentLevel] * ratio * m_modZ) / m_modZ;
767
768 float d = fdist(Vec2f(m_player->pos.x, m_player->pos.z), Vec2f(npc->pos.x, npc->pos.z));
769 if(d > 800 || fabs(ents.player()->pos.y - npc->pos.y) > 250.f) {
770 continue; // the NPC is too far away to be detected
771 }
772
773 float col = 1.f;
774
775 if(d > 600.f) {
776 col = 1.f - (d - 600.f) * ( 1.0f / 200 );
777 }
778
779 fpx *= Xratio;
780 fpy *= Yratio;
781 EERIEDrawBitmap(fpx, fpy, 5.f * ratio, 5.f * ratio, 0, m_pTexDetect,
782 Color3f(col, 0, 0).to<u8>());
783 }
784
785 GRenderer->SetRenderState(Renderer::AlphaBlending, false);
786 }
787
clearMarkerTexCont()788 void MiniMap::clearMarkerTexCont() {
789 m_mapMarkerTexCont = NULL;
790 }
791
load(const SavedMiniMap * saved,size_t size)792 void MiniMap::load(const SavedMiniMap *saved, size_t size) {
793 std::copy(saved, saved + size, m_levels);
794 }
795
save(SavedMiniMap * toSave,size_t size)796 void MiniMap::save(SavedMiniMap *toSave, size_t size) {
797 std::copy(m_levels, m_levels + size, toSave);
798 }
799
mapMarkerInit(size_t reserveSize)800 void MiniMap::mapMarkerInit(size_t reserveSize) {
801 m_mapMarkers.clear();
802
803 if(reserveSize > 0)
804 m_mapMarkers.reserve(reserveSize);
805 }
806
mapMarkerGetID(const std::string & name)807 int MiniMap::mapMarkerGetID(const std::string &name) {
808
809 for(size_t i = 0; i < m_mapMarkers.size(); i++) {
810 if(m_mapMarkers[i].m_name == name) {
811 return i;
812 }
813 }
814
815 return -1;
816 }
817
mapMarkerAdd(float x,float y,int lvl,const std::string & name)818 void MiniMap::mapMarkerAdd(float x, float y, int lvl, const std::string &name) {
819
820 int num = mapMarkerGetID(name);
821
822 if(num >= 0) {
823 // Already exists, update it
824 m_mapMarkers[num].m_lvl = lvl;
825 m_mapMarkers[num].m_x = x;
826 m_mapMarkers[num].m_y = y;
827 return;
828 }
829
830 // Else, create one
831 MapMarkerData newMMD;
832 newMMD.m_lvl = lvl;
833 newMMD.m_x = x;
834 newMMD.m_y = y;
835 newMMD.m_name = name;
836 newMMD.m_text = getLocalised(name);
837 m_mapMarkers.push_back(newMMD);
838 }
839
mapMarkerRemove(const std::string & name)840 void MiniMap::mapMarkerRemove(const std::string &name) {
841
842 int num = mapMarkerGetID(name);
843
844 if(num < 0) {
845 return; // Doesn't exist
846 }
847
848 m_mapMarkers.erase(m_mapMarkers.begin() + num);
849 }
850
mapMarkerCount()851 size_t MiniMap::mapMarkerCount() {
852 return m_mapMarkers.size();
853 }
854
mapMarkerGet(size_t id)855 MiniMap::MapMarkerData MiniMap::mapMarkerGet(size_t id) {
856
857 assert(id < m_mapMarkers.size());
858 return m_mapMarkers[id];
859 }
860
setActiveBackground(EERIE_BACKGROUND * activeBkg)861 void MiniMap::setActiveBackground(EERIE_BACKGROUND *activeBkg) {
862 m_activeBkg = activeBkg;
863 }
864