1 /*
2  * This file is part of EasyRPG Player.
3  *
4  * EasyRPG Player is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * EasyRPG Player is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 // Headers
19 #include "sprite_character.h"
20 #include "cache.h"
21 #include "game_map.h"
22 #include "bitmap.h"
23 
Sprite_Character(Game_Character * character,CloneType type)24 Sprite_Character::Sprite_Character(Game_Character* character, CloneType type) :
25 	character(character),
26 	tile_id(-1),
27 	character_index(0),
28 	chara_width(0),
29 	chara_height(0) {
30 
31 	x_shift = ((type & XClone) == XClone);
32 	y_shift = ((type & YClone) == YClone);
33 
34 	Update();
35 }
36 
Update()37 void Sprite_Character::Update() {
38 	if (tile_id != character->GetTileId() ||
39 		character_name != character->GetSpriteName() ||
40 		character_index != character->GetSpriteIndex() ||
41 		refresh_bitmap
42 	) {
43 		tile_id = character->GetTileId();
44 		character_name = character->GetSpriteName();
45 		character_index = character->GetSpriteIndex();
46 		refresh_bitmap = false;
47 
48 		if (UsesCharset()) {
49 			FileRequestAsync* char_request = AsyncHandler::RequestFile("CharSet", character_name);
50 			char_request->SetGraphicFile(true);
51 			request_id = char_request->Bind(&Sprite_Character::OnCharSpriteReady, this);
52 			char_request->Start();
53 		} else {
54 			FileRequestAsync* tile_request = AsyncHandler::RequestFile("ChipSet", Game_Map::GetChipsetName());
55 			tile_request->SetGraphicFile(true);
56 			request_id = tile_request->Bind(&Sprite_Character::OnTileSpriteReady, this);
57 			tile_request->Start();
58 		}
59 	}
60 
61 	if (UsesCharset()) {
62 		int row = character->GetFacing();
63 		auto frame = character->GetAnimFrame();
64 		if (frame >= lcf::rpg::EventPage::Frame_middle2) frame = lcf::rpg::EventPage::Frame_middle;
65 		SetSrcRect({frame * chara_width, row * chara_height, chara_width, chara_height});
66 	}
67 
68 	SetFlashEffect(character->GetFlashColor());
69 
70 	SetOpacity(character->GetOpacity());
71 	SetVisible(character->IsVisible());
72 
73 	SetX(character->GetScreenX(x_shift));
74 	SetY(character->GetScreenY(y_shift));
75 	// y_shift because Z is calculated via the screen Y position
76 	SetZ(character->GetScreenZ(y_shift));
77 
78 	int bush_split = 4 - character->GetBushDepth();
79 	SetBushDepth(bush_split > 3 ? 0 : GetHeight() / bush_split);
80 }
81 
GetCharacter()82 Game_Character* Sprite_Character::GetCharacter() {
83 	return character;
84 }
SetCharacter(Game_Character * new_character)85 void Sprite_Character::SetCharacter(Game_Character* new_character) {
86 	character = new_character;
87 }
88 
UsesCharset() const89 bool Sprite_Character::UsesCharset() const {
90 	return !character_name.empty();
91 }
92 
OnTileSpriteReady(FileRequestResult *)93 void Sprite_Character::OnTileSpriteReady(FileRequestResult*) {
94 	const auto chipset = Game_Map::GetChipsetName();
95 
96 	BitmapRef tile;
97 	if (!chipset.empty()) {
98 		tile = Cache::Tile(Game_Map::GetChipsetName(), tile_id);
99 	}
100 	else {
101 		tile = Bitmap::Create(16, 16, true);
102 	}
103 
104 	SetBitmap(tile);
105 
106 	SetSrcRect({ 0, 0, TILE_SIZE, TILE_SIZE });
107 	SetOx(8);
108 	SetOy(16);
109 
110 	Update();
111 }
112 
ChipsetUpdated()113 void Sprite_Character::ChipsetUpdated() {
114 	if (UsesCharset()) {
115 		return;
116 	}
117 	refresh_bitmap = true;
118 }
119 
GetCharacterRect(StringView name,int index,const Rect bitmap_rect)120 Rect Sprite_Character::GetCharacterRect(StringView name, int index, const Rect bitmap_rect) {
121 	Rect rect;
122 	// Allow large 4x2 spriteset of 3x4 sprites
123 	// when the character name starts with a $ sign.
124 	// This is not exactly the VX Ace way because
125 	// VX Ace uses a single 1x1 spriteset of 3x4 sprites.
126 	if (!name.empty() && name.front() == '$') {
127 		rect.width = bitmap_rect.width * (TILE_SIZE / 16) / 4;
128 		rect.height = bitmap_rect.height * (TILE_SIZE / 16) / 2;
129 	} else {
130 		rect.width = 24 * (TILE_SIZE / 16) * 3;;
131 		rect.height = 32 * (TILE_SIZE / 16) * 4;
132 	}
133 	rect.x = (index % 4) * rect.width;
134 	rect.y = (index / 4) * rect.height;
135 	return rect;
136 }
137 
OnCharSpriteReady(FileRequestResult *)138 void Sprite_Character::OnCharSpriteReady(FileRequestResult*) {
139 	SetBitmap(Cache::Charset(character_name));
140 	auto rect = GetCharacterRect(character_name, character_index, GetBitmap()->GetRect());
141 	chara_width = rect.width / 3;
142 	chara_height = rect.height / 4;
143 	SetOx(chara_width / 2);
144 	SetOy(chara_height);
145 	SetSpriteRect(rect);
146 
147 	Update();
148 }
149