/*
* Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
*
* This file is part of Arx Libertatis.
*
* Arx Libertatis is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Arx Libertatis is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Arx Libertatis. If not, see .
*/
/* Based on:
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
.
In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
// Code: Cyril Meynier
//
// Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
#include
#include
#include
#include
#include
#include
#include
#include "game/Damage.h"
#include "game/EntityManager.h"
#include "game/Equipment.h"
#include "game/Inventory.h"
#include "game/Item.h"
#include "game/NPC.h"
#include "game/Player.h"
#include "game/Spells.h"
#include "gui/Interface.h"
#include "graphics/BaseGraphicsTypes.h"
#include "graphics/Color.h"
#include "graphics/GraphicsTypes.h"
#include "graphics/Math.h"
#include "graphics/Vertex.h"
#include "graphics/data/Mesh.h"
#include "graphics/data/MeshManipulation.h"
#include "graphics/data/TextureContainer.h"
#include "graphics/particle/ParticleEffects.h"
#include "io/resource/ResourcePath.h"
#include "math/Random.h"
#include "math/Vector2.h"
#include "math/Vector3.h"
#include "physics/Collisions.h"
#include "platform/Platform.h"
#include "scene/Object.h"
#include "scene/LinkedObject.h"
#include "scene/GameSound.h"
#include "scene/Interactive.h"
#include "script/Script.h"
using std::min;
using std::max;
using std::string;
struct EQUIP_INFO
{
char name[64];
};
#define SP_SPARKING 1
#define SP_BLOODY 2
extern Vec3f PUSH_PLAYER_FORCE;
extern long HERO_SHOW_1ST;
extern long EXTERNALVIEW;
extern EERIE_3DOBJ * arrowobj;
EQUIP_INFO equipinfo[IO_EQUIPITEM_ELEMENT_Number];
//***********************************************************************************************
// Returns the object type flag corresponding to a string
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
ItemType ARX_EQUIPMENT_GetObjectTypeFlag(const string & temp) {
if(temp.empty()) {
return 0;
}
char c = temp[0];
arx_assert(std::tolower(c) == c);
switch(c) {
case 'w':
return OBJECT_TYPE_WEAPON;
case 'd':
return OBJECT_TYPE_DAGGER;
case '1':
return OBJECT_TYPE_1H;
case '2':
return OBJECT_TYPE_2H;
case 'b':
return OBJECT_TYPE_BOW;
case 's':
return OBJECT_TYPE_SHIELD;
case 'f':
return OBJECT_TYPE_FOOD;
case 'g':
return OBJECT_TYPE_GOLD;
case 'r':
return OBJECT_TYPE_RING;
case 'a':
return OBJECT_TYPE_ARMOR;
case 'h':
return OBJECT_TYPE_HELMET;
case 'l':
return OBJECT_TYPE_LEGGINGS;
}
return 0;
}
//***********************************************************************************************
// Releases Equiped Id from player
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Release(long id)
{
if (id)
{
for (long i = 0; i < MAX_EQUIPED; i++)
{
if (player.equiped[i] == id)
{
player.equiped[i] = 0;
}
}
}
}
// Releases Equipment Structure
void ARX_EQUIPMENT_ReleaseAll(Entity * io) {
if(!io || !(io->ioflags & IO_ITEM)) {
return;
}
free(io->_itemdata->equipitem), io->_itemdata->equipitem = NULL;
}
extern long EXITING;
// Recreates player mesh from scratch
static void applyTweak(EquipmentSlot equip, TweakType tw, const string & selection) {
if(!player.equiped[equip] || !ValidIONum(player.equiped[equip])) {
return;
}
Entity * io = entities.player();
arx_assert(entities[player.equiped[equip]]->tweakerinfo != NULL);
const IO_TWEAKER_INFO & tweak = *entities[player.equiped[equip]]->tweakerinfo;
if(!tweak.filename.empty()) {
res::path mesh = "graph/obj3d/interactive/npc/human_base/tweaks" / tweak.filename;
EERIE_MESH_TWEAK_Do(io, tw, mesh);
}
if(tweak.skintochange.empty() || tweak.skinchangeto.empty()) {
return;
}
res::path file = "graph/obj3d/textures" / tweak.skinchangeto;
TextureContainer * temp = TextureContainer::Load(file, TextureContainer::Level);
long mapidx = ObjectAddMap(io->obj, temp);
long sel = -1;
for(size_t i = 0; i < io->obj->selections.size(); i++) {
if(io->obj->selections[i].name == selection) {
sel = i;
break;
}
}
if(sel == -1) {
return;
}
long textochange = -1;
for(size_t i = 0; i < io->obj->texturecontainer.size(); i++) {
if(tweak.skintochange == io->obj->texturecontainer[i]->m_texName.filename()) {
textochange = i;
}
}
if(textochange == -1) {
return;
}
for(size_t i = 0; i < io->obj->facelist.size(); i++) {
if(IsInSelection(io->obj, io->obj->facelist[i].vid[0], sel) != -1
&& IsInSelection(io->obj, io->obj->facelist[i].vid[1], sel) != -1
&& IsInSelection(io->obj, io->obj->facelist[i].vid[2], sel) != -1) {
if(io->obj->facelist[i].texid == textochange) {
io->obj->facelist[i].texid = (short)mapidx;
}
}
}
}
void ARX_EQUIPMENT_RecreatePlayerMesh() {
if(EXITING) {
return;
}
Entity * io = entities.player();
if(!io) {
return;
}
if(io->obj != hero) {
delete io->obj;
}
io->obj = loadObject("graph/obj3d/interactive/npc/human_base/human_base.teo", false);
applyTweak(EQUIP_SLOT_HELMET, TWEAK_HEAD, "head");
applyTweak(EQUIP_SLOT_ARMOR, TWEAK_TORSO, "chest");
applyTweak(EQUIP_SLOT_LEGGINGS, TWEAK_LEGS, "leggings");
Entity * target = entities.player();
Entity * toequip = NULL;
if (!target) return;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
toequip = entities[player.equiped[i]];
if (toequip)
{
if (toequip->type_flags & (OBJECT_TYPE_DAGGER
| OBJECT_TYPE_1H
| OBJECT_TYPE_2H
| OBJECT_TYPE_BOW))
{
if (player.Interface & INTER_COMBATMODE)
{
ARX_EQUIPMENT_AttachPlayerWeaponToHand();
}
else
{
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
}
}
else if (toequip->type_flags & OBJECT_TYPE_SHIELD)
{
if (player.equiped[EQUIP_SLOT_SHIELD] != 0)
{
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "shield_attach", "shield_attach", toequip);
}
}
}
}
}
ARX_PLAYER_Restore_Skin();
HERO_SHOW_1ST = -1;
if (EXTERNALVIEW)
{
ARX_INTERACTIVE_Show_Hide_1st(entities.player(), 0);
}
else
{
ARX_INTERACTIVE_Show_Hide_1st(entities.player(), 1);
}
ARX_INTERACTIVE_HideGore(entities.player(), 1);
EERIE_Object_Precompute_Fast_Access(hero);
EERIE_Object_Precompute_Fast_Access(entities.player()->obj);
ARX_INTERACTIVE_RemoveGoreOnIO(entities.player());
}
void ARX_EQUIPMENT_UnEquipAllPlayer()
{
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i]) && (ValidIONum(player.equiped[i])))
{
ARX_EQUIPMENT_UnEquip(entities.player(), entities[player.equiped[i]]);
}
}
ARX_PLAYER_ComputePlayerFullStats();
}
bool ARX_EQUIPMENT_IsPlayerEquip(Entity * _pIO)
{
Entity * io = entities.player();
if (io == NULL) return false;
if (io != entities.player()) return false;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0) &&
ValidIONum(player.equiped[i]))
{
Entity * toequip = entities[player.equiped[i]];
if (toequip == _pIO)
{
return true;
}
}
}
return false;
}
//***********************************************************************************************
// flags & 1 == destroyed !
//***********************************************************************************************
void ARX_EQUIPMENT_UnEquip(Entity * target, Entity * tounequip, long flags)
{
if (target == NULL) return;
if (tounequip == NULL) return;
if (target != entities.player()) return;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i])
&& (entities[player.equiped[i]] == tounequip))
{
EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, tounequip->obj);
ARX_EQUIPMENT_Release(player.equiped[i]);
target->bbox1.x = 9999;
target->bbox2.x = -9999;
if(!flags & 1) {
if(DRAGINTER == NULL) {
ARX_SOUND_PlayInterface(SND_INVSTD);
Set_DragInter(tounequip);
} else {
giveToPlayer(tounequip);
}
}
EVENT_SENDER = tounequip;
SendIOScriptEvent(entities.player(), SM_EQUIPOUT);
EVENT_SENDER = entities.player();
SendIOScriptEvent(tounequip, SM_EQUIPOUT);
}
}
if ((tounequip->type_flags & OBJECT_TYPE_HELMET)
|| (tounequip->type_flags & OBJECT_TYPE_ARMOR)
|| (tounequip->type_flags & OBJECT_TYPE_LEGGINGS))
ARX_EQUIPMENT_RecreatePlayerMesh();
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_AttachPlayerWeaponToHand()
{
Entity * target = entities.player();
Entity * toequip = NULL;
if (!target) return;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
toequip = entities[player.equiped[i]];
if (toequip)
{
if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
|| (toequip->type_flags & OBJECT_TYPE_1H)
|| (toequip->type_flags & OBJECT_TYPE_2H)
|| (toequip->type_flags & OBJECT_TYPE_BOW)
)
{
EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "primary_attach", "primary_attach", toequip); //
return;
}
}
}
}
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_AttachPlayerWeaponToBack()
{
Entity * target = entities.player();
Entity * toequip = NULL;
if (!target) return;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
toequip = entities[player.equiped[i]];
if (toequip)
{
if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
|| (toequip->type_flags & OBJECT_TYPE_1H)
|| (toequip->type_flags & OBJECT_TYPE_2H)
|| (toequip->type_flags & OBJECT_TYPE_BOW)
)
{
if (toequip->type_flags & OBJECT_TYPE_BOW)
{
EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "test", toequip); //
return;
}
EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
return;
}
}
}
}
}
//***********************************************************************************************
//***********************************************************************************************
long ARX_EQUIPMENT_GetPlayerWeaponType()
{
Entity * io = entities.player();
if (!io) return WEAPON_BARE;
if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
{
Entity * toequip = entities[player.equiped[EQUIP_SLOT_WEAPON]];
if (toequip)
{
if (toequip->type_flags & OBJECT_TYPE_DAGGER) return WEAPON_DAGGER;
if (toequip->type_flags & OBJECT_TYPE_1H) return WEAPON_1H;
if (toequip->type_flags & OBJECT_TYPE_2H) return WEAPON_2H;
if (toequip->type_flags & OBJECT_TYPE_BOW) return WEAPON_BOW;
}
}
return WEAPON_BARE;
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_LaunchPlayerUnReadyWeapon()
{
Entity * io = entities.player();
if (!io) return;
ANIM_HANDLE * anim;
long type = ARX_EQUIPMENT_GetPlayerWeaponType();
switch (type)
{
case WEAPON_DAGGER:
anim = io->anims[ANIM_DAGGER_UNREADY_PART_1];
break;
case WEAPON_1H:
anim = io->anims[ANIM_1H_UNREADY_PART_1];
break;
case WEAPON_2H:
anim = io->anims[ANIM_2H_UNREADY_PART_1];
break;
case WEAPON_BOW:
{
anim = io->anims[ANIM_MISSILE_UNREADY_PART_1];
if (arrowobj)
{
EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, arrowobj);
}
}
break;
default:
anim = io->anims[ANIM_BARE_UNREADY];
break;
}
AcquireLastAnim(io);
ANIM_Set(&io->animlayer[1], anim);
}
//***********************************************************************************************
//***********************************************************************************************
float ARX_EQUIPMENT_ComputeDamages(Entity * io_source, Entity * io_target, float ratioaim, Vec3f * position)
{
EVENT_SENDER = io_source;
SendIOScriptEvent(io_target, SM_AGGRESSION);
if ((!io_source)
|| (!io_target))
return 0.f;
if (!(io_target->ioflags & IO_NPC))
{
if (io_target->ioflags & IO_FIX)
{
if (io_source == entities.player())
ARX_DAMAGES_DamageFIX(io_target, player.Full_damages, 0, 0);
else if (io_source->ioflags & IO_NPC)
ARX_DAMAGES_DamageFIX(io_target, io_source->_npcdata->damages, io_source->index(), 0);
else
ARX_DAMAGES_DamageFIX(io_target, 1, io_source->index(), 0);
}
return 0.f;
}
float attack, ac, damages;
float backstab = 1.f;
string _wmat = "bare";
const string * wmat = &_wmat;
string _amat = "flesh";
const string * amat = &_amat;
bool critical = false;
if(io_source == entities.player()) {
if(player.equiped[EQUIP_SLOT_WEAPON] != 0 && ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])) {
Entity * io = entities[player.equiped[EQUIP_SLOT_WEAPON]];
if(io && !io->weaponmaterial.empty()) {
wmat = &io->weaponmaterial;
}
}
attack = player.Full_damages;
if (rnd() * 100 <= (float)(player.Full_Attribute_Dexterity - 9) * 2.f + (float)(player.Full_Skill_Close_Combat * ( 1.0f / 5 )))
{
if (SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE)
critical = true;
}
else critical = false;
damages = attack * ratioaim;
if (io_target->_npcdata->npcflags & NPCFLAG_BACKSTAB)
{
if (rnd() * 100.f <= player.Full_Skill_Stealth * ( 1.0f / 2 ))
{
if (SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE)
backstab = 1.5f;
}
}
}
else
{
if (!(io_source->ioflags & IO_NPC)) return 0.f; // no NPC source...
if(!io_source->weaponmaterial.empty()){
wmat = &io_source->weaponmaterial;
}
if(io_source->_npcdata->weapon != NULL) {
Entity * iow = io_source->_npcdata->weapon;
if(!iow->weaponmaterial.empty()) {
wmat = &iow->weaponmaterial;
}
}
attack = io_source->_npcdata->tohit;
damages = io_source->_npcdata->damages * ratioaim * (rnd() * ( 1.0f / 2 ) + 0.5f);
long value = ARX_SPELLS_GetSpellOn(io_source, SPELL_CURSE);
if (value >= 0)
{
damages *= (spells[value].caster_level * 0.05f);
}
if (rnd() * 100 <= io_source->_npcdata->critical)
{
if (SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE)
critical = true;
}
else critical = false;
if (rnd() * 100.f <= (float)io_source->_npcdata->backstab_skill)
{
if (SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE)
backstab = 1.5f;
}
}
float absorb;
if (io_target == entities.player())
{
ac = player.Full_armor_class;
absorb = player.Full_Skill_Defense * ( 1.0f / 2 );
}
else
{
ac = ARX_INTERACTIVE_GetArmorClass(io_target);
absorb = io_target->_npcdata->absorb;
long value = ARX_SPELLS_GetSpellOn(io_target, SPELL_CURSE);
if (value >= 0)
{
float modif = (spells[value].caster_level * 0.05f);
ac *= modif;
absorb *= modif;
}
}
if(!io_target->armormaterial.empty()) {
amat = &io_target->armormaterial;
}
if(io_target == entities.player()) {
if(player.equiped[EQUIP_SLOT_ARMOR] > 0
&& ValidIONum(player.equiped[EQUIP_SLOT_ARMOR])) {
Entity * io = entities[player.equiped[EQUIP_SLOT_ARMOR]];
if(io && !io->armormaterial.empty()) {
amat = &io->armormaterial;
}
}
}
float dmgs = damages * backstab;
dmgs -= dmgs * absorb * 0.01f;
Vec3f pos = io_target->pos;
float power = std::min(1.f, dmgs * 0.05f) * 0.1f + 0.9f;
ARX_SOUND_PlayCollision(*amat, *wmat, power, 1.f, &pos, io_source);
float chance = 100.f - (ac - attack);
if(rnd() * 100.f > chance) {
return 0.f;
}
ARX_SOUND_PlayCollision("flesh", *wmat, power, 1.f, &pos, io_source);
if(dmgs > 0.f) {
if(critical) {
dmgs *= 1.5f;
}
if(io_target == entities.player()) {
// TODO should this be player.pos - player.baseOffset() = player.basePosition()?
Vec3f ppos = io_source->pos - (player.pos + player.baseOffset());
fnormalize(ppos);
// Push the player
PUSH_PLAYER_FORCE += ppos * -dmgs * Vec3f(1.0f / 11, 1.0f / 30, 1.0f / 11);
ppos *= 60.f;
ppos += ACTIVECAM->pos;
ARX_DAMAGES_DamagePlayer(dmgs, 0, io_source->index());
ARX_DAMAGES_DamagePlayerEquipment(dmgs);
} else {
Vec3f ppos = io_source->pos - io_target->pos;
fnormalize(ppos);
// Push the NPC
io_target->forcedmove += ppos * -dmgs;
Vec3f * pos = position ? position : &io_target->pos;
ARX_DAMAGES_DamageNPC(io_target, dmgs, io_source->index(), 0, pos);
}
}
return dmgs;
}
static float ARX_EQUIPMENT_GetSpecialValue(Entity * io, long val) {
if ((!io) || !(io->ioflags & IO_ITEM) || !io->_itemdata->equipitem) return -1;
for (long i = IO_EQUIPITEM_ELEMENT_SPECIAL_1; i <= IO_EQUIPITEM_ELEMENT_SPECIAL_4; i++)
{
if (io->_itemdata->equipitem->elements[i].special == val)
{
return (io->_itemdata->equipitem->elements[i].value);
}
}
return -1;
}
//***********************************************************************************************
// flags & 1 = blood spawn only
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
bool ARX_EQUIPMENT_Strike_Check(Entity * io_source, Entity * io_weapon, float ratioaim, long flags, long targ)
{
bool ret = false;
long source = (io_source == NULL) ? -1 : io_source->index();
long weapon = io_weapon->index();
EERIE_SPHERE sphere;
Vec3f * v0;
EXCEPTIONS_LIST_Pos = 0;
float rad;
long nbact = io_weapon->obj->actionlist.size();
float drain_life = ARX_EQUIPMENT_GetSpecialValue(io_weapon, IO_SPECIAL_ELEM_DRAIN_LIFE);
float paralyse = ARX_EQUIPMENT_GetSpecialValue(io_weapon, IO_SPECIAL_ELEM_PARALYZE);
for (long j = 0; j < nbact; j++) // TODO iterator
{
if (!ValidIONum(weapon)) return false;
rad = GetHitValue(io_weapon->obj->actionlist[j].name);
if (rad == -1) continue;
v0 = &io_weapon->obj->vertexlist3[io_weapon->obj->actionlist[j].idx].v;
sphere.origin = *v0;
sphere.radius = rad;
if (source != 0) sphere.radius += 15.f;
if (CheckEverythingInSphere(&sphere, source, targ))
{
for (size_t jj = 0; jj < MAX_IN_SPHERE_Pos; jj++)
{
if (ValidIONum(EVERYTHING_IN_SPHERE[jj])
&& (!(entities[EVERYTHING_IN_SPHERE[jj]]->ioflags & IO_BODY_CHUNK)))
{
long HIT_SPARK = 0;
EXCEPTIONS_LIST[EXCEPTIONS_LIST_Pos] = EVERYTHING_IN_SPHERE[jj];
EXCEPTIONS_LIST_Pos++;
if (EXCEPTIONS_LIST_Pos >= MAX_IN_SPHERE) EXCEPTIONS_LIST_Pos--;
Entity * target = entities[EVERYTHING_IN_SPHERE[jj]];
Vec3f pos;
Color color = Color::white;
long hitpoint = -1;
float curdist = 999999.f;
Vec3f vector = (sphere.origin - target->pos) * Vec3f(1.f, 0.5f, 1.f);
vector.normalize();
for (size_t ii = 0; ii < target->obj->facelist.size(); ii++)
{
if (target->obj->facelist[ii].facetype & POLY_HIDE) continue;
float d = dist(sphere.origin, target->obj->vertexlist3[target->obj->facelist[ii].vid[0]].v);
if (d < curdist)
{
hitpoint = target->obj->facelist[ii].vid[0];
curdist = d;
}
}
if(hitpoint >= 0) {
color = (target->ioflags & IO_NPC) ? target->_npcdata->blood_color : Color::white;
pos = target->obj->vertexlist3[hitpoint].v;
}
else ARX_DEAD_CODE();
float dmgs = 0.f;
if (!(flags & 1))
{
Vec3f posi;
if (hitpoint >= 0)
{
posi = target->obj->vertexlist3[hitpoint].v;
dmgs = ARX_EQUIPMENT_ComputeDamages(io_source, target, ratioaim, &posi);
}
else
{
dmgs = ARX_EQUIPMENT_ComputeDamages(io_source, target, ratioaim);
}
if (target->ioflags & IO_NPC)
{
ret = true;
target->spark_n_blood = 0;
target->_npcdata->SPLAT_TOT_NB = 0;
if (drain_life > 0.f)
{
float life_gain = min(dmgs, drain_life);
life_gain = min(life_gain, target->_npcdata->life);
life_gain = max(life_gain, 0.f);
ARX_DAMAGES_HealInter(io_source, life_gain);
}
if (paralyse > 0.f)
{
float ptime = min(dmgs * 1000.f, paralyse);
ARX_SPELLS_Launch(SPELL_PARALYSE, weapon, SPELLCAST_FLAG_NOMANA | SPELLCAST_FLAG_NOCHECKCANCAST
, 5, EVERYTHING_IN_SPHERE[jj], (long)(ptime));
}
}
if (io_source == entities.player())
{
ARX_DAMAGES_DurabilityCheck(io_weapon, 0.2f);
}
}
if ((dmgs > 0.f) || ((target->ioflags & IO_NPC) && (target->spark_n_blood == SP_BLOODY)))
{
if (target->ioflags & IO_NPC)
{
target->spark_n_blood = SP_BLOODY;
if (!(flags & 1))
{
ARX_PARTICLES_Spawn_Splat(pos, dmgs, color);
EERIE_SPHERE sp;
float power;
power = (dmgs * ( 1.0f / 40 )) + 0.7f;
Vec3f vect;
vect.x = target->obj->vertexlist3[hitpoint].v.x - io_source->pos.x;
vect.y = 0;
vect.z = target->obj->vertexlist3[hitpoint].v.z - io_source->pos.z;
fnormalize(vect);
sp.origin.x = target->obj->vertexlist3[hitpoint].v.x + vect.x * 30.f;
sp.origin.y = target->obj->vertexlist3[hitpoint].v.y;
sp.origin.z = target->obj->vertexlist3[hitpoint].v.z + vect.z * 30.f;
sp.radius = 3.5f * power * 20;
if(CheckAnythingInSphere(&sp, 0, CAS_NO_NPC_COL)) {
Color3f rgb = color.to();
SpawnGroundSplat(&sp, &rgb, 30, 1);
}
}
ARX_PARTICLES_Spawn_Blood2(pos, dmgs, color, target);
if (!ValidIONum(weapon)) io_weapon = NULL;
}
else
{
if (target->ioflags & IO_ITEM)
ARX_PARTICLES_Spawn_Spark(&pos, rnd() * 3.f, 0);
else
ARX_PARTICLES_Spawn_Spark(&pos, rnd() * 30.f, 0);
ARX_NPC_SpawnAudibleSound(&pos, io_source);
if (io_source == entities.player())
HIT_SPARK = 1;
}
}
else if ((target->ioflags & IO_NPC)
&& ((dmgs <= 0.f) || (target->spark_n_blood == SP_SPARKING)))
{
long nb;
if (target->spark_n_blood == SP_SPARKING)
nb = Random::get(0, 3);
else
nb = 30;
if (target->ioflags & IO_ITEM)
nb = 1;
ARX_PARTICLES_Spawn_Spark(&pos, (float)nb, 0);
ARX_NPC_SpawnAudibleSound(&pos, io_source);
target->spark_n_blood = SP_SPARKING;
if (!(target->ioflags & IO_NPC))
HIT_SPARK = 1;
}
else if ((dmgs <= 0.f)
&& ((target->ioflags & IO_FIX) || (target->ioflags & IO_ITEM)))
{
long nb;
if (target->spark_n_blood == SP_SPARKING)
nb = Random::get(0, 3);
else
nb = 30;
if (target->ioflags & IO_ITEM)
nb = 1;
ARX_PARTICLES_Spawn_Spark(&pos, (float)nb, 0);
ARX_NPC_SpawnAudibleSound(&pos, io_source);
target->spark_n_blood = SP_SPARKING;
if (!(target->ioflags & IO_NPC))
HIT_SPARK = 1;
}
if(HIT_SPARK) {
if(!io_source->isHit) {
ARX_DAMAGES_DurabilityCheck(io_weapon, 1.f);
io_source->isHit = true;
if(!ValidIONum(weapon)) {
io_weapon = NULL;
} else {
string _weapon_material = "metal";
const string * weapon_material = &_weapon_material;
if(io_weapon && !io_weapon->weaponmaterial.empty()) {
weapon_material = &io_weapon->weaponmaterial;
}
char bkg_material[128];
if (ARX_MATERIAL_GetNameById(target->material, bkg_material))
ARX_SOUND_PlayCollision(*weapon_material, bkg_material, 1.f, 1.f, &sphere.origin, NULL);
}
}
}
}
}
}
EERIEPOLY * ep = CheckBackgroundInSphere(&sphere);
if (ep)
{
if (io_source == entities.player())
{
if(!io_source->isHit) {
ARX_DAMAGES_DurabilityCheck(io_weapon, 1.f);
io_source->isHit = true;
if (!ValidIONum(weapon))
{
io_weapon = NULL;
}
else
{
string _weapon_material = "metal";
const string * weapon_material = &_weapon_material;
if(io_weapon && !io_weapon->weaponmaterial.empty()) {
weapon_material = &io_weapon->weaponmaterial;
}
std::string bkg_material = "earth";
if (ep && ep->tex && !ep->tex->m_texName.empty())
bkg_material = GetMaterialString( ep->tex->m_texName );
ARX_SOUND_PlayCollision(*weapon_material, bkg_material, 1.f, 1.f, &sphere.origin, io_source);
}
}
}
ARX_PARTICLES_Spawn_Spark(&sphere.origin, rnd() * 10.f, 0);
ARX_NPC_SpawnAudibleSound(&sphere.origin, io_source);
}
}
return ret;
}
//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
void ARX_EQUIPMENT_LaunchPlayerReadyWeapon()
{
Entity * io = entities.player();
if (!io) return;
long type = ARX_EQUIPMENT_GetPlayerWeaponType();
ANIM_HANDLE * anim = NULL;
switch (type)
{
case WEAPON_DAGGER:
anim = io->anims[ANIM_DAGGER_READY_PART_1];
break;
case WEAPON_1H:
anim = io->anims[ANIM_1H_READY_PART_1];
break;
case WEAPON_2H:
if (player.equiped[EQUIP_SLOT_SHIELD] == 0)
anim = io->anims[ANIM_2H_READY_PART_1];
break;
case WEAPON_BOW:
if (player.equiped[EQUIP_SLOT_SHIELD] == 0)
anim = io->anims[ANIM_MISSILE_READY_PART_1];
break;
default:
anim = io->anims[ANIM_BARE_READY];
break;
}
AcquireLastAnim(io);
ANIM_Set(&io->animlayer[1], anim);
}
//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
void ARX_EQUIPMENT_UnEquipPlayerWeapon()
{
if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
{
Entity * pioOldDragInter;
pioOldDragInter = DRAGINTER;
DRAGINTER = entities[player.equiped[EQUIP_SLOT_WEAPON]];
if (DRAGINTER)
ARX_SOUND_PlayInterface(SND_INVSTD);
ARX_EQUIPMENT_UnEquip(entities.player(), entities[player.equiped[EQUIP_SLOT_WEAPON]]);
DRAGINTER = pioOldDragInter;
}
player.equiped[EQUIP_SLOT_WEAPON] = 0;
}
bool bRing = false;
//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
void ARX_EQUIPMENT_Equip(Entity * target, Entity * toequip)
{
if (!target) return;
if (!toequip) return;
if (target != entities.player()) return;
long validid = -1;
for(size_t i = 0; i < entities.size(); i++) {
if(entities[i] == toequip) {
validid = i;
break;
}
}
if (validid == -1) return;
RemoveFromAllInventories(toequip);
toequip->show = SHOW_FLAG_ON_PLAYER; // on player
if (toequip == DRAGINTER)
Set_DragInter(NULL);
if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
|| (toequip->type_flags & OBJECT_TYPE_1H)
|| (toequip->type_flags & OBJECT_TYPE_2H)
|| (toequip->type_flags & OBJECT_TYPE_BOW)
)
{
if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_WEAPON]]);
}
player.equiped[EQUIP_SLOT_WEAPON] = (short)validid;
if (toequip->type_flags & OBJECT_TYPE_BOW)
{
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "test", toequip); //
}
else
{
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
}
if ((toequip->type_flags & OBJECT_TYPE_2H) || (toequip->type_flags & OBJECT_TYPE_BOW))
{
if (player.equiped[EQUIP_SLOT_SHIELD] != 0)
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_SHIELD]]);
}
}
}
else if (toequip->type_flags & OBJECT_TYPE_SHIELD)
{
if ((player.equiped[EQUIP_SLOT_SHIELD] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_SHIELD]))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_SHIELD]]);
}
player.equiped[EQUIP_SLOT_SHIELD] = (short)validid;
EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "shield_attach", "shield_attach", toequip);
if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
{
if ((entities[player.equiped[EQUIP_SLOT_WEAPON]]->type_flags & OBJECT_TYPE_2H) ||
(entities[player.equiped[EQUIP_SLOT_WEAPON]]->type_flags & OBJECT_TYPE_BOW))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_WEAPON]]);
}
}
}
else if (toequip->type_flags & OBJECT_TYPE_RING)
{
// check first, if not already equiped
if (!((ValidIONum(player.equiped[EQUIP_SLOT_RING_LEFT]) && (toequip == entities[player.equiped[EQUIP_SLOT_RING_LEFT]]))
|| (ValidIONum(player.equiped[EQUIP_SLOT_RING_RIGHT]) && (toequip == entities[player.equiped[EQUIP_SLOT_RING_RIGHT]]))))
{
long willequip = -1;
if (player.equiped[EQUIP_SLOT_RING_LEFT] == 0) willequip = EQUIP_SLOT_RING_LEFT;
if (player.equiped[EQUIP_SLOT_RING_RIGHT] == 0) willequip = EQUIP_SLOT_RING_RIGHT;
if (willequip == -1)
{
if (bRing)
{
if (ValidIONum(player.equiped[EQUIP_SLOT_RING_RIGHT]))
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_RING_RIGHT]]);
willequip = EQUIP_SLOT_RING_RIGHT;
}
else
{
if (ValidIONum(player.equiped[EQUIP_SLOT_RING_LEFT]))
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_RING_LEFT]]);
willequip = EQUIP_SLOT_RING_LEFT;
}
bRing = !bRing;
}
player.equiped[willequip] = (short)validid;
}
}
else if (toequip->type_flags & OBJECT_TYPE_ARMOR)
{
if ((player.equiped[EQUIP_SLOT_ARMOR] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_ARMOR]))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_ARMOR]]);
}
player.equiped[EQUIP_SLOT_ARMOR] = (short)validid;
}
else if (toequip->type_flags & OBJECT_TYPE_LEGGINGS)
{
if ((player.equiped[EQUIP_SLOT_LEGGINGS] != 0)
&& ValidIONum(player.equiped[EQUIP_SLOT_LEGGINGS]))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_LEGGINGS]]);
}
player.equiped[EQUIP_SLOT_LEGGINGS] = (short)validid;
}
else if (toequip->type_flags & OBJECT_TYPE_HELMET)
{
if ((player.equiped[EQUIP_SLOT_HELMET] != 0)
&& (ValidIONum(player.equiped[EQUIP_SLOT_HELMET])))
{
ARX_EQUIPMENT_UnEquip(target, entities[player.equiped[EQUIP_SLOT_HELMET]]);
}
player.equiped[EQUIP_SLOT_HELMET] = (short)validid;
}
if ((toequip->type_flags & OBJECT_TYPE_HELMET)
|| (toequip->type_flags & OBJECT_TYPE_ARMOR)
|| (toequip->type_flags & OBJECT_TYPE_LEGGINGS))
ARX_EQUIPMENT_RecreatePlayerMesh();
ARX_PLAYER_ComputePlayerFullStats();
}
bool ARX_EQUIPMENT_SetObjectType(Entity & io, const string & temp, bool set) {
ItemType flag = ARX_EQUIPMENT_GetObjectTypeFlag(temp);
if(set) {
io.type_flags |= flag;
} else {
io.type_flags &= ~flag;
}
return (flag != 0);
}
//***********************************************************************************************
// Initializes Equipment infos
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Init()
{
// IO_EQUIPITEM_ELEMENT_... are Defined in EERIEPOLY.h
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_STRENGTH].name, "strength");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_DEXTERITY].name, "dexterity");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_CONSTITUTION].name, "constitution");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MIND].name, "intelligence");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Stealth].name, "stealth");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Mecanism].name, "mecanism");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Intuition].name, "intuition");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Etheral_Link].name, "etheral_link");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Object_Knowledge].name, "object_knowledge");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Casting].name, "casting");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Projectile].name, "projectile");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Close_Combat].name, "close_combat");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Defense].name, "defense");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Armor_Class].name, "armor_class");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Resist_Magic].name, "resist_magic");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Resist_Poison].name, "resist_poison");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Critical_Hit].name, "critical_hit");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Damages].name, "damages");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Duration].name, "duration");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_AimTime].name, "aim_time");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Identify_Value].name, "identify_value");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Life].name, "life");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Mana].name, "mana");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MaxLife].name, "maxlife");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MaxMana].name, "maxmana");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_1].name, "special1");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_2].name, "special2");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_3].name, "special3");
strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_4].name, "special4");
}
//***********************************************************************************************
// Removes All special equipement properties
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Remove_All_Special(Entity * io)
{
if (!io) return;
if (!(io->ioflags & IO_ITEM)) return;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_1].special = IO_SPECIAL_ELEM_NONE;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_2].special = IO_SPECIAL_ELEM_NONE;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_3].special = IO_SPECIAL_ELEM_NONE;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_4].special = IO_SPECIAL_ELEM_NONE;
}
//***********************************************************************************************
// Sets an equipment property
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
float ARX_EQUIPMENT_Apply(Entity * io, EquipmentModifierType ident,
float trueval) {
if (io == NULL) return trueval;
if (io != entities.player()) return trueval;
float toadd = 0;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
Entity * toequip = entities[player.equiped[i]];
if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
{
IO_EQUIPITEM_ELEMENT * elem = &toequip->_itemdata->equipitem->elements[ident];
if (!(elem->flags & IO_ELEMENT_FLAG_PERCENT))
toadd += elem->value;
}
}
}
return toadd;
}
float ARX_EQUIPMENT_ApplyPercent(Entity * io, EquipmentModifierType ident,
float trueval) {
if (io == NULL) return trueval;
if (io != entities.player()) return trueval;
float toadd = 0;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
Entity * toequip = entities[player.equiped[i]];
if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
{
IO_EQUIPITEM_ELEMENT * elem = &toequip->_itemdata->equipitem->elements[ident];
if (elem->flags & IO_ELEMENT_FLAG_PERCENT) // percentile value...
{
toadd += elem->value;
}
}
}
}
return (toadd * trueval * ( 1.0f / 100 ));
}
void ARX_EQUIPMENT_SetEquip(Entity * io, bool special,
const std::string & param2, float val,
EquipmentModifierFlags flags) {
if (io == NULL) return;
if (!(io->ioflags & IO_ITEM)) return;
if (!io->_itemdata->equipitem)
{
io->_itemdata->equipitem = (IO_EQUIPITEM *) malloc(sizeof(IO_EQUIPITEM));
if (io->_itemdata->equipitem == NULL) return;
memset(io->_itemdata->equipitem, 0, sizeof(IO_EQUIPITEM));
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Duration].value = 10;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_AimTime].value = 10;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value = 0;
io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value = 0;
}
if(special) {
for (long i = IO_EQUIPITEM_ELEMENT_SPECIAL_1; i <= IO_EQUIPITEM_ELEMENT_SPECIAL_4; i++)
{
if (io->_itemdata->equipitem->elements[i].special == IO_SPECIAL_ELEM_NONE)
{
if(param2 == "paralyse") {
io->_itemdata->equipitem->elements[i].special = IO_SPECIAL_ELEM_PARALYZE;
} else if(param2 == "drainlife") {
io->_itemdata->equipitem->elements[i].special = IO_SPECIAL_ELEM_DRAIN_LIFE;
}
io->_itemdata->equipitem->elements[i].value = val;
io->_itemdata->equipitem->elements[i].flags = flags;
return;
}
}
return;
} else {
for(long i = 0; i < IO_EQUIPITEM_ELEMENT_Number; i++) {
if(param2 == equipinfo[i].name) {
io->_itemdata->equipitem->elements[i].value = val;
io->_itemdata->equipitem->elements[i].special = IO_SPECIAL_ELEM_NONE;
io->_itemdata->equipitem->elements[i].flags = flags;
return;
}
}
}
}
//-----------------------------------------------------------------------------
void ARX_EQUIPMENT_IdentifyAll()
{
Entity * io = entities.player();
if (io == NULL) return;
if (io != entities.player()) return;
for (long i = 0; i < MAX_EQUIPED; i++)
{
if ((player.equiped[i] != 0)
&& ValidIONum(player.equiped[i]))
{
Entity * toequip = entities[player.equiped[i]];
if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
{
if (player.Full_Skill_Object_Knowledge + player.Full_Attribute_Mind
>= toequip->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value)
{
SendIOScriptEvent(toequip, SM_IDENTIFY);
}
}
}
}
}
float GetHitValue( const std::string & name) {
if(boost::starts_with(name, "hit_")) {
// Get the number after the first 4 characters in the string
try {
return float(boost::lexical_cast(name.substr(4)));
} catch(...) { /* ignore */ }
}
return -1;
}