Subversion Repositories eoserv

Compare Revisions

Ignore whitespace Rev 476 → Rev 477

/trunk/config/console.ini
17,3 → 17,8
## StyleConsole (bool)
# Uses pretty colors for Out/Warn/Error/Debug
StyleConsole = yes
 
## LogCommands (bool)
# Logs the use of admin commands
LogCommands = yes
 
/trunk/config/rates.ini
52,3 → 52,45
TPRecoverRate = 10%
SitTPRecoverRate = 20%
NPCRecoverRate = 10%
 
 
## SPIKES / DRAINS ##
 
## SpikeTime (number)
# How frequently timed spikes will activate
# 0 to disable timed spikes
SpikeTime = 1.5s
 
## SpikeDamage (number)
# What amount of health to remove from a character being hit by spikes
# 0 to disable spike damage
SpikeDamage = 20%
 
## DrainTime (number)
# How frequently map drain effects will activate
# 0 to disable map drains
DrainTime = 15s
 
## DrainHPDamage (number)
# Amount of HP drained for map HP drains
# 0 to disable HP drains
DrainHPDamage = 20%
 
## DrainTPDamage (number)
# Amount of TP drained for map TP drains
# 0 to disable TP drains
DrainTPDamage = 10%
 
## QuakeRate (number)
# How frequently earthquake ticks will happen
# 0 to disable quakes
QuakeRate = 5s
 
## QuakeX (number[4])
# List of rates and strength ranges for map earthquake effects (4 types)
# Format: minticks,maxticks,minstrength,maxstrength
Quake1 = 4,12,0,1
Quake2 = 6,12,0,2
Quake3 = 2,10,3,5
Quake4 = 1,4,6,8
 
/trunk/src/character.cpp
551,12 → 551,12
this->player->Send(builder);
}
 
bool Character::Walk(Direction direction)
Map::WalkResult Character::Walk(Direction direction)
{
return this->map->Walk(this, direction);
}
 
bool Character::AdminWalk(Direction direction)
Map::WalkResult Character::AdminWalk(Direction direction)
{
return this->map->Walk(this, direction, true);
}
1766,6 → 1766,66
this->quests[id].reset();
}
 
void Character::SpikeDamage(int amount)
{
int limitamount = std::min(amount, int(this->hp));
 
if (this->world->config["LimitDamage"])
{
amount = limitamount;
}
 
this->hp -= limitamount;
 
PacketBuilder builder2(PACKET_EFFECT, PACKET_SPEC, 7);
builder2.AddChar(2);
builder2.AddShort(amount);
builder2.AddShort(this->hp);
builder2.AddShort(this->maxhp);
 
PacketBuilder builder3(PACKET_EFFECT, PACKET_ADMIN, 7);
builder3.AddShort(this->player->id);
builder3.AddChar(util::clamp<int>(double(this->hp) / double(this->maxhp) * 100.0, 0, 100));
builder3.AddChar(this->hp == 0);
builder3.AddThree(amount);
 
this->Send(builder2);
 
for (Character* watcher : this->map->CharactersInRange(this->x, this->y, static_cast<int>(this->world->config["SeeDistance"])))
{
if (watcher == this)
continue;
 
watcher->Send(builder3);
}
}
 
 
void Character::DeathRespawn()
{
this->hp = int(this->maxhp * static_cast<double>(this->world->config["DeathRecover"]) / 100.0);
 
if (this->world->config["Deadly"])
{
this->DropAll(nullptr);
}
 
this->map->Leave(this, WARP_ANIMATION_NONE, true);
this->nowhere = true;
this->map = this->world->GetMap(this->SpawnMap());
this->mapid = this->SpawnMap();
this->x = this->SpawnX();
this->y = this->SpawnY();
 
this->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_NULL), char(PACKET_INTERNAL)}
}.data()), 1.5);
 
this->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_WARP), char(PACKET_INTERNAL)}
}.data()), 0.0);
}
 
void Character::Mute(const Command_Source *by)
{
this->muted_until = time(0) + int(this->world->config["MuteLength"]);
/trunk/src/character.hpp
295,8 → 295,8
void Msg(Character *from, std::string message);
void ServerMsg(std::string message);
void StatusMsg(std::string message);
bool Walk(Direction direction);
bool AdminWalk(Direction direction);
Map::WalkResult Walk(Direction direction);
Map::WalkResult AdminWalk(Direction direction);
void Attack(Direction direction);
void Sit(SitState sit_type);
void Stand();
320,7 → 320,8
bool Equip(short item, unsigned char subloc);
bool InRange(unsigned char x, unsigned char y) const;
bool InRange(const Character *) const;
bool InRange(const NPC *) const; bool InRange(const Map_Item&) const;
bool InRange(const NPC *) const;
bool InRange(const Map_Item&) const;
void Warp(short map, unsigned char x, unsigned char y, WarpAnimation animation = WARP_ANIMATION_NONE);
void Refresh();
void ShowBoard(Board *board = 0);
339,6 → 340,9
std::shared_ptr<Quest_Context> GetQuest(short id);
void ResetQuest(short id);
 
void SpikeDamage(int amount);
void DeathRespawn();
 
void Mute(const Command_Source *by);
void PlaySound(unsigned char id);
 
/trunk/src/commands/moderation.cpp
73,7 → 73,7
{
Character* victim = from->SourceWorld()->GetCharacter(arguments[0]);
 
if (victim->mapid != static_cast<int>(from->SourceWorld()->config["JailMap"]))
if (victim && victim->mapid != static_cast<int>(from->SourceWorld()->config["JailMap"]))
{
from->ServerMsg(from->SourceWorld()->i18n.Format("command_access_denied"));
return;
/trunk/src/eoserv_config.cpp
25,6 → 25,7
eoserv_config_default(config, "LogOut" , "-");
eoserv_config_default(config, "LogErr" , "error.log");
eoserv_config_default(config, "StyleConsole" , true);
eoserv_config_default(config, "LogCommands" , true);
eoserv_config_default(config, "Host" , "0.0.0.0");
eoserv_config_default(config, "Port" , 8078);
eoserv_config_default(config, "MaxConnections" , 300);
151,6 → 152,16
eoserv_config_default(config, "TPRecoverRate" , 0.1);
eoserv_config_default(config, "SitTPRecoverRate" , 0.2);
eoserv_config_default(config, "NPCRecoverRate" , 0.1);
eoserv_config_default(config, "SpikeTime" , 1.5);
eoserv_config_default(config, "SpikeDamage" , 0.2);
eoserv_config_default(config, "DrainTime" , 15);
eoserv_config_default(config, "DrainHPDamage" , 0.2);
eoserv_config_default(config, "DrainTPDamage" , 0.1);
eoserv_config_default(config, "QuakeRate" , 5);
eoserv_config_default(config, "Quake1" , "4,12,0,1");
eoserv_config_default(config, "Quake2" , "6,12,0,2");
eoserv_config_default(config, "Quake3" , "2,10,3,5");
eoserv_config_default(config, "Quake4" , "1,4,6,8");
eoserv_config_default(config, "ChatLength" , 128);
eoserv_config_default(config, "ShareMode" , 2);
eoserv_config_default(config, "PartyShareMode" , 2);
/trunk/src/handlers/Talk.cpp
119,6 → 119,11
 
if (character->SourceAccess() && message[0] == '$')
{
if (character->world->config["LogCommands"])
{
Console::Out("%s: %s", character->SourceName().c_str(), message.c_str());
}
 
std::string command;
std::vector<std::string> arguments = util::explode(' ', message);
command = arguments.front().substr(1);
/trunk/src/handlers/Walk.cpp
15,12 → 15,13
namespace Handlers
{
 
static void walk_common(Character *character, PacketReader &reader, bool (Character::*f)(Direction))
static void walk_common(Character *character, PacketReader &reader, Map::WalkResult (Character::*f)(Direction))
{
Direction direction = static_cast<Direction>(reader.GetChar());
Timestamp timestamp = reader.GetThree();
unsigned char x = reader.GetChar();
unsigned char y = reader.GetChar();
Map::WalkResult walk_result;
 
if (character->world->config["EnforceTimestamps"])
{
65,13 → 66,10
character->trade_partner = 0;
}
 
if (!(character->*f)(direction))
{
return;
}
walk_result = (character->*f)(direction);
}
 
if (character->x != x || character->y != y)
if (walk_result == Map::WalkFail || (walk_result == Map::WalkOK && (character->x != x || character->y != y)))
{
character->Refresh();
}
/trunk/src/map.cpp
133,16 → 133,22
}
else
{
std::vector<Character*> evac_chars;
 
restart_loop:
UTIL_FOREACH(evac->map->characters, character)
{
if (character->SourceAccess() < ADMIN_GUIDE)
{
character->world->Jail(0, character, false);
goto restart_loop;
evac_chars.push_back(character);
}
}
 
UTIL_FOREACH(evac_chars, character)
{
character->world->Jail(0, character, false);
}
 
evac->map->evacuate_lock = false;
}
}
324,6 → 330,7
this->jukebox_protect = 0.0;
this->arena = 0;
this->evacuate_lock = false;
this->has_timed_spikes = false;
 
this->LoadArena();
 
445,6 → 452,8
return false;
}
 
this->has_timed_spikes = false;
 
SAFE_SEEK(fh, 0x03, SEEK_SET);
SAFE_READ(this->rid, sizeof(char), 4, fh);
 
453,8 → 462,9
unsigned char innersize;
 
SAFE_SEEK(fh, 0x1F, SEEK_SET);
SAFE_READ(buf, sizeof(char), 1, fh);
SAFE_READ(buf, sizeof(char), 2, fh);
this->pk = PacketProcessor::Number(buf[0]) == 3;
this->effect = static_cast<EffectType>(PacketProcessor::Number(buf[1]));
 
SAFE_SEEK(fh, 0x25, SEEK_SET);
SAFE_READ(buf, sizeof(char), 2, fh);
522,6 → 532,11
chest.slots = 0;
this->chests.push_back(std::make_shared<Map_Chest>(chest));
}
 
if (spec == Map_Tile::Spikes1)
{
this->has_timed_spikes = true;
}
}
}
 
825,7 → 840,7
}
}
 
bool Map::Walk(Character *from, Direction direction, bool admin)
Map::WalkResult Map::Walk(Character *from, Direction direction, bool admin)
{
int seedistance = this->world->config["SeeDistance"];
 
839,7 → 854,7
 
if (target_y > from->y)
{
return false;
return WalkFail;
}
 
break;
849,7 → 864,7
 
if (target_x < from->x)
{
return false;
return WalkFail;
}
 
break;
859,7 → 874,7
 
if (target_x < from->x)
{
return false;
return WalkFail;
}
 
break;
869,7 → 884,7
 
if (target_x > from->x)
{
return false;
return WalkFail;
}
 
break;
876,15 → 891,15
}
 
if (!this->InBounds(target_x, target_y))
return false;
return WalkFail;
 
if (!admin)
{
if (!this->Walkable(target_x, target_y))
return false;
return WalkFail;
 
if (this->Occupied(target_x, target_y, PlayerOnly) && (from->last_walk + double(this->world->config["GhostTimer"]) > Timer::GetTime()))
return false;
return WalkFail;
}
 
const Map_Warp& warp = this->GetWarp(target_x, target_y);
904,11 → 919,11
from->Warp(warp.map, warp.x, warp.y);
}
 
return false;
return WalkWarped;
}
 
if (!admin)
return false;
return WalkFail;
}
 
from->last_walk = Timer::GetTime();
1169,10 → 1184,27
 
from->CheckQuestRules();
 
return true;
Map_Tile::TileSpec spec = this->GetSpec(from->x, from->y);
 
double spike_damage = this->world->config["SpikeDamage"];
 
if (spike_damage > 0.0 && (spec == Map_Tile::Spikes2 || spec == Map_Tile::Spikes3) && !from->IsHideInvisible())
{
int amount = from->maxhp * spike_damage;
 
from->SpikeDamage(amount);
}
 
if (from->hp == 0)
{
from->DeathRespawn();
return WalkWarped;
}
 
return WalkOK;
}
 
bool Map::Walk(NPC *from, Direction direction)
Map::WalkResult Map::Walk(NPC *from, Direction direction)
{
int seedistance = this->world->config["SeeDistance"];
 
1186,7 → 1218,7
 
if (target_y > from->y)
{
return false;
return WalkFail;
}
 
break;
1196,7 → 1228,7
 
if (target_x < from->x)
{
return false;
return WalkFail;
}
 
break;
1206,7 → 1238,7
 
if (target_x < from->x)
{
return false;
return WalkFail;
}
 
break;
1216,7 → 1248,7
 
if (target_x > from->x)
{
return false;
return WalkFail;
}
 
break;
1224,7 → 1256,7
 
if (!this->Walkable(target_x, target_y, true) || this->Occupied(target_x, target_y, Map::PlayerAndNPC))
{
return false;
return WalkFail;
}
 
from->x = target_x;
1354,7 → 1386,7
from->RemoveFromView(character);
}
 
return true;
return WalkOK;
}
 
void Map::Attack(Character *from, Direction direction)
1582,28 → 1614,8
 
if (character->hp == 0)
{
character->hp = int(character->maxhp * static_cast<double>(this->world->config["DeathRecover"]) / 100.0);
character->DeathRespawn();
 
if (this->world->config["Deadly"])
{
character->DropAll(from);
}
 
character->map->Leave(character, WARP_ANIMATION_NONE, true);
character->nowhere = true;
character->map = this->world->GetMap(character->SpawnMap());
character->mapid = character->SpawnMap();
character->x = character->SpawnX();
character->y = character->SpawnY();
 
character->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_NULL), char(PACKET_INTERNAL)}
}.data()), 1.5);
 
character->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_WARP), char(PACKET_INTERNAL)}
}.data()), 0.0);
 
UTIL_FOREACH(from->quests, q)
{
if (!q.second || q.second->GetQuest()->Disabled())
1975,28 → 1987,8
 
if (victim->hp == 0)
{
victim->hp = int(victim->maxhp * static_cast<double>(this->world->config["DeathRecover"]) / 100.0);
victim->DeathRespawn();
 
if (this->world->config["Deadly"])
{
victim->DropAll(from);
}
 
victim->map->Leave(victim, WARP_ANIMATION_NONE, true);
victim->nowhere = true;
victim->map = this->world->GetMap(victim->SpawnMap());
victim->mapid = victim->SpawnMap();
victim->x = victim->SpawnX();
victim->y = victim->SpawnY();
 
victim->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_NULL), char(PACKET_INTERNAL)}
}.data()), 1.5);
 
victim->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_WARP), char(PACKET_INTERNAL)}
}.data()), 0.0);
 
UTIL_FOREACH(from->quests, q)
{
if (!q.second || q.second->GetQuest()->Disabled())
2422,6 → 2414,8
SAFE_SEEK(fh, 0x03, SEEK_SET);
SAFE_READ(checkrid, sizeof(char), 4, fh);
 
std::fclose(fh);
 
if (this->rid[0] == checkrid[0] && this->rid[1] == checkrid[1]
&& this->rid[2] == checkrid[2] && this->rid[3] == checkrid[3])
{
2452,6 → 2446,141
return true;
}
 
void Map::TimedSpikes()
{
if (!this->has_timed_spikes)
return;
 
PacketBuilder builder(PACKET_EFFECT, PACKET_REPORT, 1);
builder.AddByte(83); // S
 
double spike_damage = this->world->config["SpikeDamage"];
 
std::vector<Character*> killed;
 
for (Character* character : this->characters)
{
if (character->nowhere || character->IsHideInvisible())
continue;
 
Map_Tile::TileSpec spec = this->GetSpec(character->x, character->y);
 
if (spike_damage > 0.0 && spec == Map_Tile::Spikes1)
{
int amount = character->maxhp * spike_damage;
 
character->SpikeDamage(amount);
 
if (character->hp == 0)
killed.push_back(character);
}
else
{
character->Send(builder);
}
}
 
for (Character* character : killed)
{
character->DeathRespawn();
}
}
 
void Map::TimedDrains()
{
if (this->effect == EffectHPDrain)
{
double hpdrain_damage = this->world->config["DrainHPDamage"];
 
std::vector<int> damage_map;
damage_map.resize(this->characters.size());
 
std::size_t i = 0;
 
for (Character* character : this->characters)
{
if (character->nowhere || character->IsHideInvisible())
continue;
 
int amount = character->maxhp * hpdrain_damage;
amount = std::max(std::min(amount, int(character->hp - 1)), 0);
character->hp -= amount;
 
damage_map[i++] = amount;
}
 
i = 0;
 
for (Character* character : this->characters)
{
if (character->nowhere || character->IsHideInvisible())
continue;
 
if (hpdrain_damage > 0.0)
{
int damage = damage_map[i++];
 
PacketBuilder builder(PACKET_EFFECT, PACKET_TARGET_OTHER, 6 + this->characters.size() * 5);
builder.AddShort(damage);
builder.AddShort(character->hp);
builder.AddShort(character->maxhp);
 
std::size_t ii = 0;
 
for (Character* other : this->characters)
{
if (other->nowhere || other->IsHideInvisible())
continue;
 
int damage = damage_map[ii++];
 
if (!character->InRange(other) || other == character)
continue;
 
builder.AddShort(other->player->id);
builder.AddChar(util::clamp<int>(double(other->hp) / double(other->maxhp) * 100.0, 0, 100));
builder.AddShort(damage);
}
 
character->Send(builder);
}
}
}
if (this->effect == EffectTPDrain)
{
double tpdrain_damage = this->world->config["DrainTPDamage"];
 
for (Character* character : this->characters)
{
if (character->nowhere || character->IsHideInvisible())
continue;
 
if (tpdrain_damage > 0.0)
{
int amount = character->maxtp * tpdrain_damage;
amount = std::min(amount, int(character->tp));
 
character->tp -= amount;
 
PacketBuilder builder(PACKET_EFFECT, PACKET_SPEC, 7);
builder.AddChar(1);
builder.AddShort(amount);
builder.AddShort(character->tp);
builder.AddShort(character->maxtp);
 
character->Send(builder);
}
}
}
}
 
void Map::TimedQuakes()
{
 
}
 
Character *Map::GetCharacter(std::string name)
{
name = util::lowercase(name);
/trunk/src/map.hpp
212,10 → 212,29
void Unload();
 
public:
enum WalkResult
{
WalkFail = 0,
WalkOK = 1,
WalkWarped = 2
};
 
enum EffectType
{
EffectNone = 0,
EffectHPDrain = 1,
EffectTPDrain = 2,
EffectQuake1 = 3,
EffectQuake2 = 4,
EffectQuake3 = 5,
EffectQuake4 = 6
};
 
World *world;
short id;
char rid[4];
bool pk;
EffectType effect;
int filesize;
unsigned char width;
unsigned char height;
231,6 → 250,7
double jukebox_protect;
std::string jukebox_player;
bool evacuate_lock;
bool has_timed_spikes;
 
Arena *arena;
 
245,7 → 265,7
 
void Msg(Character *from, std::string message, bool echo = true);
void Msg(NPC *from, std::string message);
bool Walk(Character *from, Direction direction, bool admin = false);
WalkResult Walk(Character *from, Direction direction, bool admin = false);
void Attack(Character *from, Direction direction);
bool AttackPK(Character *from, Direction direction);
void Face(Character *from, Direction direction);
260,7 → 280,7
void SpellAttackPK(Character *from, Character *victim, unsigned short spell_id);
void SpellGroup(Character *from, unsigned short spell_id);
 
bool Walk(NPC *from, Direction direction);
WalkResult Walk(NPC *from, Direction direction);
 
std::shared_ptr<Map_Item> AddItem(short id, int amount, unsigned char x, unsigned char y, Character *from = 0);
 
288,6 → 308,10
 
bool Reload();
 
void TimedSpikes();
void TimedDrains();
void TimedQuakes();
 
Character *GetCharacter(std::string name);
Character *GetCharacterPID(unsigned int id);
Character *GetCharacterCID(unsigned int id);
/trunk/src/npc.cpp
542,7 → 542,7
}
}
 
if (!this->Walk(this->direction))
if (this->Walk(this->direction) == Map::WalkFail)
{
this->Walk(static_cast<Direction>(util::rand(0,3)));
}
1198,47 → 1198,15
character->Send(builder);
}
 
int rechp = int(target->maxhp * static_cast<double>(this->map->world->config["DeathRecover"]));
 
if (target->hp == 0)
{
builder.AddShort(rechp);
target->DeathRespawn();
}
else
{
builder.AddShort(target->hp);
}
 
builder.AddShort(target->hp);
builder.AddShort(target->tp);
 
target->Send(builder);
 
if (target->hp == 0)
{
target->hp = rechp;
 
if (this->map->world->config["Deadly"])
{
target->DropAll(0);
}
 
target->map->Leave(target, WARP_ANIMATION_NONE, true);
 
target->nowhere = true;
target->map = this->map->world->GetMap(target->SpawnMap());
target->mapid = target->SpawnMap();
target->x = target->SpawnX();
target->y = target->SpawnY();
 
PacketReader reader("");
 
target->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_NULL), char(PACKET_INTERNAL)}
}.data()), 1.5);
 
target->player->client->queue.AddAction(PacketReader(std::array<char, 2>{
{char(PACKET_INTERNAL_WARP), char(PACKET_INTERNAL)}
}.data()), 0.0);
}
}
 
#define v(x) vars[prefix + #x] = x;
/trunk/src/world.cpp
234,6 → 234,36
world->BeginDB();
}
 
void world_spikes(void *world_void)
{
World *world = static_cast<World *>(world_void);
for (Map* map : world->maps)
{
map->TimedSpikes();
}
}
 
void world_drains(void *world_void)
{
World *world = static_cast<World *>(world_void);
for (Map* map : world->maps)
{
map->TimedDrains();
}
}
 
void world_quakes(void *world_void)
{
World *world = static_cast<World *>(world_void);
for (Map* map : world->maps)
{
map->TimedQuakes();
}
}
 
void World::UpdateConfig()
{
this->timer.SetMaxDelta(this->config["ClockMaxDelta"]);
425,6 → 455,24
this->timer.Register(event);
}
 
if (this->config["SpikeTime"])
{
event = new TimeEvent(world_spikes, this, static_cast<double>(this->config["SpikeTime"]), Timer::FOREVER);
this->timer.Register(event);
}
 
if (this->config["DrainTime"])
{
event = new TimeEvent(world_drains, this, static_cast<double>(this->config["DrainTime"]), Timer::FOREVER);
this->timer.Register(event);
}
 
if (this->config["QuakeRate"])
{
event = new TimeEvent(world_quakes, this, static_cast<double>(this->config["QuakeRate"]), Timer::FOREVER);
this->timer.Register(event);
}
 
exp_table[0] = 0;
for (std::size_t i = 1; i < this->exp_table.size(); ++i)
{