1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D 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 2 of the License, or
9 // (at your option) any later version.
10 //
11 // Scorched3D 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 along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
21 #include <common/ToolTip.h>
22 #include <common/ModelID.h>
23 #include <weapons/Accessory.h>
24 #include <weapons/AccessoryStore.h>
25 #include <common/Defines.h>
26 #include <common/OptionsScorched.h>
27 #include <lang/LangResource.h>
28 #include <tank/Tank.h>
29 #include <tank/TankModel.h>
30 #include <tank/TankModelContainer.h>
31 #include <stdlib.h>
32
33 unsigned int Accessory::nextAccessoryId_ = 0;
34
35 #ifndef S3D_SERVER
36 #include <3dsparse/Model.h>
37 #include <3dsparse/ModelStore.h>
38 #include <tankgraph/MissileMesh.h>
39 #include <image/ImageFactory.h>
40 #endif
41
Accessory()42 Accessory::Accessory() :
43 accessoryId_(++nextAccessoryId_),
44 name_("NONAME"), description_("NODESC"),
45 accessoryAction_(0),
46 toolTip_(ToolTip::ToolTipHelp, LangString(), LangString()),
47 price_(0), bundle_(1), armsLevel_(9), freemarketLimits_(150),
48 modelScale_(1),
49 positionSelect_(ePositionSelectNone), positionSelectLimit_(10),
50 maximumNumber_(0),
51 startingNumber_(0),
52 useNumber_(1),
53 muzzleFlash_(true),
54 aiOnly_(false),
55 botOnly_(false),
56 noBuy_(false)
57 {
58 }
59
~Accessory()60 Accessory::~Accessory()
61 {
62 delete accessoryAction_;
63 accessoryAction_ = 0;
64 }
65
parseXML(AccessoryCreateContext & context,XMLNode * accessoryNode)66 bool Accessory::parseXML(AccessoryCreateContext &context, XMLNode *accessoryNode)
67 {
68 // Get the accessory name
69 if (!accessoryNode->getNamedChild("name", name_)) return false;
70
71 // Get the accessory armslevel
72 accessoryNode->getNamedChild("armslevel", armsLevel_, false);
73
74 // Get the optional muzzleflash
75 XMLNode *muzzleFlashNode = 0;
76 accessoryNode->getNamedChild("nomuzzleflash", muzzleFlashNode, false);
77 if (muzzleFlashNode) muzzleFlash_ = false;
78
79 // Get the accessory description
80 accessoryNode->getNamedChild("description", description_, false);
81 toolTip_.setText(ToolTip::ToolTipHelp, LANG_STRING(getName()), LANG_STRING(getDescription()));
82
83 // Get the accessory icon
84 if (accessoryNode->getNamedChild("icon", iconName_, false))
85 {
86 if (!S3D::checkDataFile(S3D::formatStringBuffer("data/textures/wicons/%s", getIconName()))) return false;
87 }
88
89 #ifndef S3D_SERVER
90 if (getIconName()[0])
91 {
92 texture_.setImageID(ImageID(
93 S3D::eModLocation,
94 "",
95 S3D::formatStringBuffer("data/textures/wicons/%s", getIconName())));
96 }
97 else
98 {
99 texture_.setImageID(ImageID(
100 S3D::eModLocation,
101 "",
102 S3D::formatStringBuffer("data/textures/wicons/%s", "tracer.bmp")));
103 }
104 #endif
105
106 // Get the accessory sound
107 if (accessoryNode->getNamedChild("activationsound", activationSound_, false))
108 {
109 if (!S3D::checkDataFile(S3D::formatStringBuffer("data/wav/%s", getActivationSound()))) return false;
110 }
111
112 // Get the accessory bundle
113 accessoryNode->getNamedChild("bundlesize", bundle_, false);
114
115 // Get ai only
116 accessoryNode->getNamedChild("aionly", aiOnly_, false);
117
118 // Get bot only (ie: only for bots, not for tank objects)
119 accessoryNode->getNamedChild("botonly", botOnly_, false);
120
121 // Get no buy
122 accessoryNode->getNamedChild("nobuy", noBuy_, false);
123
124 // Get the maximum number
125 maximumNumber_ = context.getOptionsGame().getMaxNumberWeapons();
126 accessoryNode->getNamedChild("maximumnumber", maximumNumber_, false);
127
128 // Get the starting number
129 accessoryNode->getNamedChild("startingnumber", startingNumber_, false);
130
131 // Get the number to use of firing
132 accessoryNode->getNamedChild("usenumber", useNumber_, false);
133
134 // Freemarket limits
135 accessoryNode->getNamedChild("freemarketlimits", freemarketLimits_, false);
136
137 // Get the accessory cost
138 accessoryNode->getNamedChild("cost", price_, false);
139
140 // Get the weapon model scale
141 accessoryNode->getNamedChild("modelscale", modelScale_, false);
142
143 // Get the weapon model
144 XMLNode *modelNode = 0;
145 if (accessoryNode->getNamedChild("model", modelNode, false))
146 {
147 if (!modelId_.initFromNode(modelNode)) return false;
148 }
149
150 // Get action
151 XMLNode *subNode = 0;
152 if (!accessoryNode->getNamedChild("accessoryaction", subNode)) return false;
153 accessoryAction_ = context.getAccessoryStore().createAccessoryPart(context, this, subNode);
154 if (!accessoryAction_)
155 {
156 S3D::dialogMessage("Accessory", S3D::formatStringBuffer(
157 "Failed to create action \"%s\"", name_.c_str()));
158 return false;
159 }
160
161 // Setup price
162 sellPrice_ = 0;
163 if (price_ > 0 && bundle_ > 0) sellPrice_ = int((price_ / bundle_) * 0.8f);
164 originalPrice_ = price_;
165 originalSellPrice_ = sellPrice_;
166
167 // Position Selection Type
168 std::string positionSelection;
169 if (accessoryNode->getNamedChild("positionselection", positionSelection, false))
170 {
171 if (0 == strcmp(positionSelection.c_str(), "none"))
172 {
173 positionSelect_ = ePositionSelectNone;
174 }
175 else if (0 == strcmp(positionSelection.c_str(), "generic"))
176 {
177 positionSelect_ = ePositionSelectGeneric;
178 }
179 else if (0 == strcmp(positionSelection.c_str(), "fuel"))
180 {
181 positionSelect_ = ePositionSelectFuel;
182
183 // Make sure there is a "WeaponMoveTank" under here somewhere
184 if (!context.getAccessoryStore().findAccessoryPartByAccessoryId(
185 getAccessoryId(), "WeaponMoveTank"))
186 {
187 return accessoryNode->returnError(
188 "Fuel selection can only be used with WeaponMoveTank weapons");
189 }
190 }
191 else if (0 == strcmp(positionSelection.c_str(), "fuellimit"))
192 {
193 positionSelect_ = ePositionSelectFuelLimit;
194 if (!accessoryNode->getNamedChild("positionselectionlimit", positionSelectLimit_)) return false;
195 }
196 else if (0 == strcmp(positionSelection.c_str(), "limit"))
197 {
198 positionSelect_ = ePositionSelectLimit;
199 if (!accessoryNode->getNamedChild("positionselectionlimit", positionSelectLimit_)) return false;
200 }
201 else
202 {
203 return accessoryNode->returnError(S3D::formatStringBuffer(
204 "Unknown accessory position selection type \"%s\"",
205 positionSelection.c_str()));
206 }
207 }
208
209 // Get the accessory tabgroupname
210 if (!accessoryNode->getNamedChild("tabgroup", tabGroupName_, false))
211 {
212 if (accessoryAction_->getType() == AccessoryPart::AccessoryWeapon)
213 {
214 tabGroupName_ = "weapon";
215 }
216 else
217 {
218 tabGroupName_ = "defense";
219 }
220 }
221
222 // Get the accessory groupname
223 if (!accessoryNode->getNamedChild("group", groupName_, false))
224 {
225 switch (accessoryAction_->getType())
226 {
227 case AccessoryPart::AccessoryWeapon:
228 groupName_ = "weapon";
229 break;
230 case AccessoryPart::AccessoryParachute:
231 groupName_ = "parachute";
232 break;
233 case AccessoryPart::AccessoryShield:
234 groupName_ = "shield";
235 break;
236 case AccessoryPart::AccessoryAutoDefense:
237 groupName_ = "autodefense";
238 break;
239 case AccessoryPart::AccessoryBattery:
240 groupName_ = "battery";
241 break;
242 default:
243 groupName_ = "none";
244 break;
245 }
246 }
247
248 return true;
249 }
250
getStringName()251 LangString &Accessory::getStringName()
252 {
253 if (stringName_.size() == 0)
254 {
255 stringName_ = LANG_RESOURCE(getName(), getName());
256 }
257 return stringName_;
258 }
259
getActivationSound()260 const char *Accessory::getActivationSound()
261 {
262 if (!activationSound_.c_str()[0]) return 0;
263 return activationSound_.c_str();
264 }
265
266 #ifndef S3D_SERVER
267
268 std::map<std::string, MissileMesh *> Accessory::loadedMeshes_;
269
getWeaponMesh(ModelID & id,Tank * currentPlayer)270 MissileMesh *Accessory::getWeaponMesh(ModelID &id, Tank *currentPlayer)
271 {
272 // Set the default model to use if neither the tank
273 // or weapon have one
274 static ModelID defaultModelId;
275 if (!defaultModelId.modelValid())
276 {
277 defaultModelId.initFromString(
278 "MilkShape",
279 "data/accessories/v2missile/v2missile.txt",
280 "");
281 }
282
283 // Set the model to use as the default model id
284 ModelID *usedModelId = &defaultModelId;
285
286 // Get the model to use from the weapon (if there is one)
287 if (id.modelValid())
288 {
289 usedModelId = &id;
290 }
291 else
292 {
293 // The weapon does not have a model defined for it
294 // check the player to see if they have a default model
295 if (currentPlayer)
296 {
297 TankModel *model = currentPlayer->getModelContainer().getTankModel();
298 if (model &&
299 model->getProjectileModelID().modelValid())
300 {
301 usedModelId = &model->getProjectileModelID();
302 }
303 }
304 }
305
306 // Load or find the correct missile mesh
307 MissileMesh *mesh = 0;
308 const char *name = usedModelId->getStringHash();
309 std::map<std::string, MissileMesh *>::iterator itor =
310 loadedMeshes_.find(name);
311 if (itor == loadedMeshes_.end())
312 {
313 mesh = new MissileMesh(*usedModelId);
314 loadedMeshes_[name] = mesh;
315 }
316 else
317 {
318 // Find
319 mesh = (*itor).second;
320 }
321 return mesh;
322 }
323 #endif // S3D_SERVER
324