1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ags/shared/ac/common.h"
24 #include "ags/engine/ac/object.h"
25 #include "ags/engine/ac/character.h"
26 #include "ags/engine/ac/game_state.h"
27 #include "ags/shared/ac/game_setup_struct.h"
28 #include "ags/engine/ac/object.h"
29 #include "ags/engine/ac/room.h"
30 #include "ags/engine/ac/room_object.h"
31 #include "ags/engine/ac/room_status.h"
32 #include "ags/engine/ac/walkable_area.h"
33 #include "ags/shared/game/room_struct.h"
34 #include "ags/shared/gfx/bitmap.h"
35 #include "ags/globals.h"
36 
37 namespace AGS3 {
38 
39 using namespace AGS::Shared;
40 
redo_walkable_areas()41 void redo_walkable_areas() {
42 	_GP(thisroom).WalkAreaMask->Blit(_G(walkareabackup), 0, 0, 0, 0, _GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight());
43 
44 	int hh, ww;
45 	for (hh = 0; hh < _G(walkareabackup)->GetHeight(); hh++) {
46 		uint8_t *walls_scanline = _GP(thisroom).WalkAreaMask->GetScanLineForWriting(hh);
47 		for (ww = 0; ww < _G(walkareabackup)->GetWidth(); ww++) {
48 			//      if (_GP(play).walkable_areas_on[_getpixel(_GP(thisroom).WalkAreaMask,ww,hh)]==0)
49 			if (_GP(play).walkable_areas_on[walls_scanline[ww]] == 0)
50 				walls_scanline[ww] = 0;
51 		}
52 	}
53 
54 }
55 
get_walkable_area_pixel(int x,int y)56 int get_walkable_area_pixel(int x, int y) {
57 	return _GP(thisroom).WalkAreaMask->GetPixel(room_to_mask_coord(x), room_to_mask_coord(y));
58 }
59 
get_area_scaling(int onarea,int xx,int yy)60 int get_area_scaling(int onarea, int xx, int yy) {
61 
62 	int zoom_level = 100;
63 	xx = room_to_mask_coord(xx);
64 	yy = room_to_mask_coord(yy);
65 
66 	if ((onarea >= 0) && (onarea <= MAX_WALK_AREAS) &&
67 	        (_GP(thisroom).WalkAreas[onarea].ScalingNear != NOT_VECTOR_SCALED)) {
68 		// We have vector scaling!
69 		// In case the character is off the screen, limit the Y co-ordinate
70 		// to within the area range (otherwise we get silly zoom levels
71 		// that cause Out Of Memory crashes)
72 		if (yy > _GP(thisroom).WalkAreas[onarea].Bottom)
73 			yy = _GP(thisroom).WalkAreas[onarea].Bottom;
74 		if (yy < _GP(thisroom).WalkAreas[onarea].Top)
75 			yy = _GP(thisroom).WalkAreas[onarea].Top;
76 		// Work it all out without having to use floats
77 		// Percent = ((y - top) * 100) / (areabottom - areatop)
78 		// Zoom level = ((max - min) * Percent) / 100
79 		if (_GP(thisroom).WalkAreas[onarea].Bottom != _GP(thisroom).WalkAreas[onarea].Top) {
80 			int percent = ((yy - _GP(thisroom).WalkAreas[onarea].Top) * 100)
81 			              / (_GP(thisroom).WalkAreas[onarea].Bottom - _GP(thisroom).WalkAreas[onarea].Top);
82 			zoom_level = ((_GP(thisroom).WalkAreas[onarea].ScalingNear - _GP(thisroom).WalkAreas[onarea].ScalingFar) * (percent)) / 100 + _GP(thisroom).WalkAreas[onarea].ScalingFar;
83 		} else {
84 			// Special case for 1px tall walkable area: take bottom line scaling
85 			zoom_level = _GP(thisroom).WalkAreas[onarea].ScalingNear;
86 		}
87 		zoom_level += 100;
88 	} else if ((onarea >= 0) & (onarea <= MAX_WALK_AREAS))
89 		zoom_level = _GP(thisroom).WalkAreas[onarea].ScalingFar + 100;
90 
91 	if (zoom_level == 0)
92 		zoom_level = 100;
93 
94 	return zoom_level;
95 }
96 
scale_sprite_size(int sppic,int zoom_level,int * newwidth,int * newheight)97 void scale_sprite_size(int sppic, int zoom_level, int *newwidth, int *newheight) {
98 	newwidth[0] = (_GP(game).SpriteInfos[sppic].Width * zoom_level) / 100;
99 	newheight[0] = (_GP(game).SpriteInfos[sppic].Height * zoom_level) / 100;
100 	if (newwidth[0] < 1)
101 		newwidth[0] = 1;
102 	if (newheight[0] < 1)
103 		newheight[0] = 1;
104 }
105 
remove_walkable_areas_from_temp(int fromx,int cwidth,int starty,int endy)106 void remove_walkable_areas_from_temp(int fromx, int cwidth, int starty, int endy) {
107 
108 	fromx = room_to_mask_coord(fromx);
109 	cwidth = room_to_mask_coord(cwidth);
110 	starty = room_to_mask_coord(starty);
111 	endy = room_to_mask_coord(endy);
112 
113 	int yyy;
114 	if (endy >= _G(walkable_areas_temp)->GetHeight())
115 		endy = _G(walkable_areas_temp)->GetHeight() - 1;
116 	if (starty < 0)
117 		starty = 0;
118 
119 	for (; cwidth > 0; cwidth--) {
120 		for (yyy = starty; yyy <= endy; yyy++)
121 			_G(walkable_areas_temp)->PutPixel(fromx, yyy, 0);
122 		fromx++;
123 	}
124 
125 }
126 
is_point_in_rect(int x,int y,int left,int top,int right,int bottom)127 int is_point_in_rect(int x, int y, int left, int top, int right, int bottom) {
128 	if ((x >= left) && (x < right) && (y >= top) && (y <= bottom))
129 		return 1;
130 	return 0;
131 }
132 
prepare_walkable_areas(int sourceChar)133 Bitmap *prepare_walkable_areas(int sourceChar) {
134 	// copy the walkable areas to the temp bitmap
135 	_G(walkable_areas_temp)->Blit(_GP(thisroom).WalkAreaMask.get(), 0, 0, 0, 0, _GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight());
136 	// if the character who's moving doesn't Bitmap *, don't bother checking
137 	if (sourceChar < 0);
138 	else if (_GP(game).chars[sourceChar].flags & CHF_NOBLOCKING)
139 		return _G(walkable_areas_temp);
140 
141 	int ww;
142 	// for each character in the current room, make the area under
143 	// them unwalkable
144 	for (ww = 0; ww < _GP(game).numcharacters; ww++) {
145 		if (_GP(game).chars[ww].on != 1) continue;
146 		if (_GP(game).chars[ww].room != _G(displayed_room)) continue;
147 		if (ww == sourceChar) continue;
148 		if (_GP(game).chars[ww].flags & CHF_NOBLOCKING) continue;
149 		if (room_to_mask_coord(_GP(game).chars[ww].y) >= _G(walkable_areas_temp)->GetHeight()) continue;
150 		if (room_to_mask_coord(_GP(game).chars[ww].x) >= _G(walkable_areas_temp)->GetWidth()) continue;
151 		if ((_GP(game).chars[ww].y < 0) || (_GP(game).chars[ww].x < 0)) continue;
152 
153 		CharacterInfo *char1 = &_GP(game).chars[ww];
154 		int cwidth, fromx;
155 
156 		if (is_char_on_another(sourceChar, ww, &fromx, &cwidth))
157 			continue;
158 		if ((sourceChar >= 0) && (is_char_on_another(ww, sourceChar, nullptr, nullptr)))
159 			continue;
160 
161 		remove_walkable_areas_from_temp(fromx, cwidth, char1->get_blocking_top(), char1->get_blocking_bottom());
162 	}
163 
164 	// check for any blocking objects in the room, and deal with them
165 	// as well
166 	for (ww = 0; ww < _G(croom)->numobj; ww++) {
167 		if (_G(objs)[ww].on != 1) continue;
168 		if ((_G(objs)[ww].flags & OBJF_SOLID) == 0)
169 			continue;
170 		if (room_to_mask_coord(_G(objs)[ww].y) >= _G(walkable_areas_temp)->GetHeight()) continue;
171 		if (room_to_mask_coord(_G(objs)[ww].x) >= _G(walkable_areas_temp)->GetWidth()) continue;
172 		if ((_G(objs)[ww].y < 0) || (_G(objs)[ww].x < 0)) continue;
173 
174 		int x1, y1, width, y2;
175 		get_object_blocking_rect(ww, &x1, &y1, &width, &y2);
176 
177 		// if the character is currently standing on the object, ignore
178 		// it so as to allow him to escape
179 		if ((sourceChar >= 0) &&
180 		        (is_point_in_rect(_GP(game).chars[sourceChar].x, _GP(game).chars[sourceChar].y,
181 		                          x1, y1, x1 + width, y2)))
182 			continue;
183 
184 		remove_walkable_areas_from_temp(x1, width, y1, y2);
185 	}
186 
187 	return _G(walkable_areas_temp);
188 }
189 
190 // return the walkable area at the character's feet, taking into account
191 // that he might just be off the edge of one
get_walkable_area_at_location(int xx,int yy)192 int get_walkable_area_at_location(int xx, int yy) {
193 
194 	int onarea = get_walkable_area_pixel(xx, yy);
195 
196 	if (onarea < 0) {
197 		// the character has walked off the edge of the screen, so stop them
198 		// jumping up to full size when leaving
199 		if (xx >= _GP(thisroom).Width)
200 			onarea = get_walkable_area_pixel(_GP(thisroom).Width - 1, yy);
201 		else if (xx < 0)
202 			onarea = get_walkable_area_pixel(0, yy);
203 		else if (yy >= _GP(thisroom).Height)
204 			onarea = get_walkable_area_pixel(xx, _GP(thisroom).Height - 1);
205 		else if (yy < 0)
206 			onarea = get_walkable_area_pixel(xx, 1);
207 	}
208 	if (onarea == 0) {
209 		// the path finder sometimes slightly goes into non-walkable areas;
210 		// so check for scaling in adjacent pixels
211 		const int TRYGAP = 2;
212 		onarea = get_walkable_area_pixel(xx + TRYGAP, yy);
213 		if (onarea <= 0)
214 			onarea = get_walkable_area_pixel(xx - TRYGAP, yy);
215 		if (onarea <= 0)
216 			onarea = get_walkable_area_pixel(xx, yy + TRYGAP);
217 		if (onarea <= 0)
218 			onarea = get_walkable_area_pixel(xx, yy - TRYGAP);
219 		if (onarea < 0)
220 			onarea = 0;
221 	}
222 
223 	return onarea;
224 }
225 
get_walkable_area_at_character(int charnum)226 int get_walkable_area_at_character(int charnum) {
227 	CharacterInfo *chin = &_GP(game).chars[charnum];
228 	return get_walkable_area_at_location(chin->x, chin->y);
229 }
230 
231 } // namespace AGS3
232