Skip to content

Commit

Permalink
feat: attached effect and shader
Browse files Browse the repository at this point in the history
  • Loading branch information
mehah committed Apr 5, 2023
2 parents e9a453e + 54dbfca commit fd3c39a
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 6 deletions.
27 changes: 27 additions & 0 deletions README.md
Expand Up @@ -3,6 +3,33 @@ forgottenserver

The Forgotten Server is a free and open-source MMORPG server emulator written in C++. It is a fork of the [OpenTibia Server](https://github.com/opentibia/server) project. To connect to the server, you can use [OTClient](https://github.com/edubart/otclient), [OpenTibiaUnity](https://github.com/slavidodo/OpenTibia-Unity) or [The Forgotten Client](https://github.com/SaiyansKing/The-Forgotten-Client)(work in progress).

### Features

- Attached Effect Support
- Shader Support

#### sample
```lua
item:setShader(<shader name>)
creature:setShader(<shader name>)
creature:attachEffectById(<effect id>, <temporary>(true | false)) -- Temporary = does not save in character
```

##### note: use storage to save a permanent effect on the player.
```lua
-- Sample
local EFFECT_STORAGE = 80000

player:setStorageValue(EFFECT_STORAGE, 7)
.
.
.
local effectId = player:getStorageValue(EFFECT_STORAGE)
if effectId ~= nil then
creature:attachEffectById(effectId)
end
```

### Getting Started

* [Compiling](https://github.com/otland/forgottenserver/wiki/Compiling)
Expand Down
7 changes: 7 additions & 0 deletions data/creaturescripts/scripts/login.lua
Expand Up @@ -29,6 +29,13 @@ function onLogin(player)
player:setVocation(vocation:getDemotion())
end

if player:getGroup():getAccess() and player:getAccountType() >= ACCOUNT_TYPE_GAMEMASTER then
player:setShader("Outfit - Rainbow")
player:attachEffectById(7)
player:attachEffectById(8)
player:attachEffectById(9, true) -- Temporary Effect
end

-- Events
player:registerEvent('PlayerDeath')
player:registerEvent('DropLoot')
Expand Down
17 changes: 17 additions & 0 deletions src/creature.cpp
Expand Up @@ -1619,4 +1619,21 @@ bool Creature::getPathTo(const Position& targetPos, std::vector<Direction>& dirL
fpp.minTargetDist = minTargetDist;
fpp.maxTargetDist = maxTargetDist;
return getPathTo(targetPos, dirList, fpp);
}

void Creature::attachEffectById(uint16_t id) {
auto it = std::find(attachedEffectList.begin(), attachedEffectList.end(), id);
if (it != attachedEffectList.end())
return;

attachedEffectList.push_back(id);
g_game.sendAttachedEffect(this, id);
}
void Creature::detachEffectById(uint16_t id) {
auto it = std::find(attachedEffectList.begin(), attachedEffectList.end(), id);
if (it == attachedEffectList.end())
return;

attachedEffectList.erase(it);
g_game.sendDetachEffect(this, id);
}
11 changes: 11 additions & 0 deletions src/creature.h
Expand Up @@ -481,6 +481,9 @@ class Creature : virtual public Thing
bool getPathTo(const Position& targetPos, std::vector<Direction>& dirList, const FindPathParams& fpp) const;
bool getPathTo(const Position& targetPos, std::vector<Direction>& dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = 0) const;

std::string getShader() const { return shader; }
void setShader(const std::string& shaderName) { shader = shaderName; }

void incrementReferenceCounter() {
++referenceCounter;
}
Expand All @@ -490,6 +493,10 @@ class Creature : virtual public Thing
}
}

void attachEffectById(uint16_t id);
void detachEffectById(uint16_t id);
const std::vector<uint16_t> getAttachedEffectList() const { return attachedEffectList; }

protected:
virtual bool useCacheMap() const {
return false;
Expand All @@ -511,6 +518,8 @@ class Creature : virtual public Thing
using CountMap = std::map<uint32_t, CountBlock_t>;
CountMap damageMap;

std::string shader;

std::vector<Creature*> summons;
CreatureEventList eventsList;
ConditionList conditions;
Expand Down Expand Up @@ -549,6 +558,8 @@ class Creature : virtual public Thing
Direction direction = DIRECTION_SOUTH;
Skulls_t skull = SKULL_NONE;

std::vector<uint16_t> attachedEffectList;

bool localMapCache[mapWalkHeight][mapWalkWidth] = { { false } };
bool isInternalRemoved = false;
bool isMapLoaded = false;
Expand Down
72 changes: 72 additions & 0 deletions src/game.cpp
Expand Up @@ -6905,4 +6905,76 @@ bool Game::reload(const ReloadTypes_t reloadType)
return true;
}
}
}

void Game::sendAttachedEffect(const Creature* creature, uint16_t effectId)
{
SpectatorVector spectators;
map.getSpectators(spectators, creature->getPosition(), false, true);
for (Creature* spectator : spectators) {
spectator->getPlayer()->sendAttachedEffect(creature, effectId);
}
}

void Game::sendDetachEffect(const Creature* creature, uint16_t effectId)
{
SpectatorVector spectators;
map.getSpectators(spectators, creature->getPosition(), false, true);
for (Creature* spectator : spectators) {
spectator->getPlayer()->sendDetachEffect(creature, effectId);
}
}

void Game::updateCreatureShader(const Creature* creature) {
SpectatorVector spectators;
map.getSpectators(spectators, creature->getPosition(), false, true);
for (Creature* spectator : spectators) {
spectator->getPlayer()->sendShader(creature, creature->getShader());
}
}

void Game::refreshItem(const Item* item) {
if (!item || !item->getParent())
return;

const auto parent = item->getParent();

if (const auto creature = parent->getCreature()) {
if (const auto player = creature->getPlayer()) {
int32_t index = creature->getPlayer()->getThingIndex(item);
if (index > -1)
player->sendInventoryItem(static_cast<slots_t>(index), item);
}

return;
}

if (const auto container = parent->getContainer()) {
int32_t index = container->getThingIndex(item);
if (index > -1) {
SpectatorVector spectators;
g_game.map.getSpectators(spectators, container->getPosition(), false, true, 2, 2, 2, 2);

//send to client
for (auto spectator : spectators) {
spectator->getPlayer()->sendUpdateContainerItem(container, index, item);
}
}

return;
}

if (const auto tile = parent->getTile()) {

SpectatorVector spectators;
g_game.map.getSpectators(spectators, tile->getPosition(), true, true);

//send to client
for (auto spectator : spectators) {
if (const Player* tmpPlayer = spectator->getPlayer()) {
tmpPlayer->sendUpdateTileItem(tile, tile->getPosition(), item);
}
}
return;
}
}
5 changes: 5 additions & 0 deletions src/game.h
Expand Up @@ -546,6 +546,11 @@ class Game
void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect);
static void addDistanceEffect(const SpectatorVector& spectators, const Position& fromPos, const Position& toPos, uint8_t effect);

void sendAttachedEffect(const Creature* creature, uint16_t effectId);
void sendDetachEffect(const Creature* creature, uint16_t effectId);
void updateCreatureShader(const Creature* creature);
void refreshItem(const Item* item);

void updateCreatureData(const Creature* creature) const;

void startDecay(Item* item);
Expand Down
19 changes: 19 additions & 0 deletions src/item.h
Expand Up @@ -1097,6 +1097,25 @@ class Item : virtual public Thing
return !parent || parent->isRemoved();
}

void setShader(const std::string& shaderName) {
if (shaderName.empty()) {
removeCustomAttribute("shader");
return;
}

ItemAttributes::CustomAttribute val;
val.set<std::string>(shaderName);

std::string key = "shader";
setCustomAttribute(key, val);
}

bool hasShader() const { return getCustomAttribute("shader") != nullptr; }
std::string getShader() const {
auto shader = getCustomAttribute("shader");
return shader ? shader->getString() : "";
}

protected:
Cylinder* parent = nullptr;

Expand Down
118 changes: 117 additions & 1 deletion src/luascript.cpp
Expand Up @@ -2558,6 +2558,10 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Item", "hasProperty", luaItemHasProperty);
registerMethod("Item", "isLoadedFromMap", luaItemIsLoadedFromMap);

registerMethod("Item", "setShader", luaItemSetShader);
registerMethod("Item", "getShader", luaItemGetShader);
registerMethod("Item", "hasShader", luaItemHasShader);

// Container
registerClass("Container", "Item", luaContainerCreate);
registerMetaMethod("Container", "__eq", luaUserdataCompare);
Expand Down Expand Up @@ -2616,6 +2620,9 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Creature", "getLight", luaCreatureGetLight);
registerMethod("Creature", "setLight", luaCreatureSetLight);

registerMethod("Creature", "getShader", luaCreatureGetShader);
registerMethod("Creature", "setShader", luaCreatureSetShader);

registerMethod("Creature", "getSpeed", luaCreatureGetSpeed);
registerMethod("Creature", "getBaseSpeed", luaCreatureGetBaseSpeed);
registerMethod("Creature", "changeSpeed", luaCreatureChangeSpeed);
Expand Down Expand Up @@ -2661,6 +2668,9 @@ void LuaScriptInterface::registerFunctions()

registerMethod("Creature", "getZone", luaCreatureGetZone);

registerMethod("Creature", "attachEffectById", LuaScriptInterface::luaCreatureAttachEffectById);
registerMethod("Creature", "detachEffectById", LuaScriptInterface::luaCreatureDetachEffectById);

// Player
registerClass("Player", "Creature", luaPlayerCreate);
registerMetaMethod("Player", "__eq", luaUserdataCompare);
Expand Down Expand Up @@ -7167,6 +7177,48 @@ int LuaScriptInterface::luaItemIsLoadedFromMap(lua_State* L)
return 1;
}

int LuaScriptInterface::luaItemHasShader(lua_State* L)
{
// item:getShader()
const auto* item = getUserdata<const Item>(L, 1);
if (item) {
pushBoolean(L, item->hasShader());
} else {
lua_pushnil(L);
}

return 1;
}

int LuaScriptInterface::luaItemGetShader(lua_State* L)
{
// item:getShader()
const auto* item = getUserdata<const Item>(L, 1);
if (item) {
pushString(L, item->getShader());
} else {
lua_pushnil(L);
}

return 1;
}

int LuaScriptInterface::luaItemSetShader(lua_State* L)
{
// item:setShader(shaderName)
auto* item = getUserdata<Item>(L, 1);
if (!item) {
lua_pushnil(L);
return 1;
}

item->setShader(getString(L, 2));
g_game.refreshItem(item);

pushBoolean(L, true);
return 1;
}

// Container
int LuaScriptInterface::luaContainerCreate(lua_State* L)
{
Expand Down Expand Up @@ -16949,4 +17001,68 @@ void LuaEnvironment::executeTimerEvent(const uint32_t eventIndex)
for (const auto parameter : timerEventDesc.parameters) {
luaL_unref(luaState, LUA_REGISTRYINDEX, parameter);
}
}
}

int LuaScriptInterface::luaCreatureAttachEffectById(lua_State* L)
{
// creature:attachEffectById(effectId, [temporary])
Creature* creature = getUserdata<Creature>(L, 1);
if (!creature) {
lua_pushnil(L);
return 1;
}

uint16_t id = getNumber<uint16_t>(L, 2);
bool temp = getBoolean(L, 3, false);

if (temp)
g_game.sendAttachedEffect(creature, id);
else
creature->attachEffectById(id);

return 1;
}

int LuaScriptInterface::luaCreatureDetachEffectById(lua_State* L)
{
// creature:detachEffectById(effectId)
Creature* creature = getUserdata<Creature>(L, 1);
if (!creature) {
lua_pushnil(L);
return 1;
}

uint16_t id = getNumber<uint16_t>(L, 2);
creature->detachEffectById(id);

return 1;
}

int LuaScriptInterface::luaCreatureGetShader(lua_State* L)
{
// creature:getShader()
const auto* creature = getUserdata<const Creature>(L, 1);
if (creature) {
pushString(L, creature->getShader());
} else {
lua_pushnil(L);
}

return 1;
}

int LuaScriptInterface::luaCreatureSetShader(lua_State* L)
{
// creature:setShader(shaderName)
auto* creature = getUserdata<Creature>(L, 1);
if (!creature) {
lua_pushnil(L);
return 1;
}

creature->setShader(getString(L, 2));
g_game.updateCreatureShader(creature);

pushBoolean(L, true);
return 1;
}

0 comments on commit fd3c39a

Please sign in to comment.