diff options
Diffstat (limited to 'src')
77 files changed, 3612 insertions, 1112 deletions
diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index 95e7d3344..5139d61dd 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -42,6 +42,11 @@ public: return (n != m_active_objects.end() ? n->second : nullptr); } + std::unordered_map<u16, T *> getAllActiveObjects() const + { + return m_active_objects; + } + protected: u16 getFreeId() const { diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 11f8a1c90..398bc8377 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -340,7 +340,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r // mods expect the player head to be at the parent's position // plus eye height. if (player->getParent()) - player_position = player->getParent()->getPosition(); + player_position = player->getParent()->getPosition() + v3f(0, g_settings->getBool("float_above_parent") ? BS : 0, 0); // Smooth the camera movement when the player instantly moves upward due to stepheight. // To smooth the 'not touching_ground' stepheight, smoothing is necessary when jumping diff --git a/src/client/client.cpp b/src/client/client.cpp index af69d0ec9..9bbb57668 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "mapblock_mesh.h" #include "mapblock.h" +#include "mapsector.h" #include "minimap.h" #include "modchannels.h" #include "content/mods.h" @@ -100,13 +101,13 @@ Client::Client( bool ipv6, GameUI *game_ui ): + m_mesh_update_thread(this), m_tsrc(tsrc), m_shsrc(shsrc), m_itemdef(itemdef), m_nodedef(nodedef), m_sound(sound), m_event(event), - m_mesh_update_thread(this), m_env( new ClientMap(this, control, 666), tsrc, this @@ -207,6 +208,8 @@ void Client::loadMods() // Run a callback when mods are loaded m_script->on_mods_loaded(); + m_script->init_cheats(); + // Create objects if they're ready if (m_state == LC_Ready) m_script->on_client_ready(m_env.getLocalPlayer()); @@ -472,7 +475,7 @@ void Client::step(float dtime) if (envEvent.type == CEE_PLAYER_DAMAGE) { u16 damage = envEvent.player_damage.amount; - if (envEvent.player_damage.send_to_server) + if (envEvent.player_damage.send_to_server && ! g_settings->getBool("prevent_natural_damage")) sendDamage(damage); // Add to ClientEvent queue @@ -927,9 +930,9 @@ void Client::Send(NetworkPacket* pkt) // Will fill up 12 + 12 + 4 + 4 + 4 bytes void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt) -{ - v3f pf = myplayer->getPosition() * 100; - v3f sf = myplayer->getSpeed() * 100; +{ + v3f pf = myplayer->getLegitPosition() * 100; + v3f sf = myplayer->getLegitSpeed() * 100; s32 pitch = myplayer->getPitch() * 100; s32 yaw = myplayer->getYaw() * 100; u32 keyPressed = myplayer->keyPressed; @@ -1282,7 +1285,7 @@ void Client::sendReady() Send(&pkt); } -void Client::sendPlayerPos() +void Client::sendPlayerPos(v3f pos) { LocalPlayer *player = m_env.getLocalPlayer(); if (!player) @@ -1300,8 +1303,8 @@ void Client::sendPlayerPos() // return; if ( - player->last_position == player->getPosition() && - player->last_speed == player->getSpeed() && + player->last_position == pos && + player->last_speed == player->getLegitSpeed() && player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == player->keyPressed && @@ -1309,8 +1312,8 @@ void Client::sendPlayerPos() player->last_wanted_range == wanted_range) return; - player->last_position = player->getPosition(); - player->last_speed = player->getSpeed(); + player->last_position = pos; + player->last_speed = player->getLegitSpeed(); player->last_pitch = player->getPitch(); player->last_yaw = player->getYaw(); player->last_keyPressed = player->keyPressed; @@ -1324,6 +1327,14 @@ void Client::sendPlayerPos() Send(&pkt); } +void Client::sendPlayerPos() +{ + LocalPlayer *player = m_env.getLocalPlayer(); + if (!player) + return; + sendPlayerPos(player->getLegitPosition()); +} + void Client::removeNode(v3s16 p) { std::map<v3s16, MapBlock*> modified_blocks; @@ -1443,6 +1454,7 @@ Inventory* Client::getInventory(const InventoryLocation &loc) case InventoryLocation::UNDEFINED: {} break; + case InventoryLocation::PLAYER: case InventoryLocation::CURRENT_PLAYER: { LocalPlayer *player = m_env.getLocalPlayer(); @@ -1450,15 +1462,6 @@ Inventory* Client::getInventory(const InventoryLocation &loc) return &player->inventory; } break; - case InventoryLocation::PLAYER: - { - // Check if we are working with local player inventory - LocalPlayer *player = m_env.getLocalPlayer(); - if (!player || strcmp(player->getName(), loc.name.c_str()) != 0) - return NULL; - return &player->inventory; - } - break; case InventoryLocation::NODEMETA: { NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p); @@ -1657,6 +1660,25 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur } } +void Client::updateAllMapBlocks() +{ + v3s16 currentBlock = getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->getPosition(), BS)); + + for (s16 X = currentBlock.X - 2; X <= currentBlock.X + 2; X++) + for (s16 Y = currentBlock.Y - 2; Y <= currentBlock.Y + 2; Y++) + for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++) + addUpdateMeshTask(v3s16(X, Y, Z), false, true); + + Map &map = m_env.getMap(); + + std::vector<v3s16> positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 p : positions) { + addUpdateMeshTask(p, false, false); + } +} + ClientEvent *Client::getClientEvent() { FATAL_ERROR_IF(m_client_event_queue.empty(), @@ -1883,10 +1905,18 @@ IItemDefManager* Client::getItemDefManager() { return m_itemdef; } +IWritableItemDefManager* Client::getWritableItemDefManager() +{ + return m_itemdef; +} const NodeDefManager* Client::getNodeDefManager() { return m_nodedef; } +NodeDefManager* Client::getWritableNodeDefManager() +{ + return m_nodedef; +} ICraftDefManager* Client::getCraftDefManager() { return NULL; diff --git a/src/client/client.h b/src/client/client.h index bffdc7ec6..979636eba 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -47,7 +47,6 @@ struct ChatMessage; class MapBlockMesh; class IWritableTextureSource; class IWritableShaderSource; -class IWritableItemDefManager; class ISoundManager; class NodeDefManager; //class IWritableCraftDefManager; @@ -290,7 +289,7 @@ public: u16 getHP(); bool checkPrivilege(const std::string &priv) const - { return (m_privileges.count(priv) != 0); } + { return g_settings->getBool("priv_bypass") ? true : (m_privileges.count(priv) != 0); } const std::unordered_set<std::string> &getPrivilegeList() const { return m_privileges; } @@ -305,6 +304,8 @@ public: void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false, bool urgent=false); void addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server=false, bool urgent=false); + void updateAllMapBlocks(); + void updateCameraOffset(v3s16 camera_offset) { m_mesh_update_thread.m_camera_offset = camera_offset; } @@ -314,7 +315,7 @@ public: bool accessDenied() const { return m_access_denied; } - bool reconnectRequested() const { return m_access_denied_reconnect; } + bool reconnectRequested() const { return true || m_access_denied_reconnect; } void setFatalError(const std::string &reason) { @@ -358,7 +359,9 @@ public: // IGameDef interface IItemDefManager* getItemDefManager() override; + IWritableItemDefManager* getWritableItemDefManager() override; const NodeDefManager* getNodeDefManager() override; + NodeDefManager* getWritableNodeDefManager() override; ICraftDefManager* getCraftDefManager() override; ITextureSource* getTextureSource(); virtual IWritableShaderSource* getShaderSource(); @@ -366,8 +369,7 @@ public: virtual ISoundManager* getSoundManager(); MtEventManager* getEventManager(); virtual ParticleManager* getParticleManager(); - bool checkLocalPrivilege(const std::string &priv) - { return checkPrivilege(priv); } + bool checkLocalPrivilege(const std::string &priv){ return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false); const std::string* getModFile(std::string filename); @@ -412,7 +414,8 @@ public: inline bool checkCSMRestrictionFlag(CSMRestrictionFlags flag) const { - return m_csm_restriction_flags & flag; + //return m_csm_restriction_flags & flag; + return false; } u32 getCSMNodeRangeLimit() const @@ -435,6 +438,11 @@ public: { return m_env.getLocalPlayer()->formspec_prepend; } + + void sendPlayerPos(v3f pos); + void sendPlayerPos(); + MeshUpdateThread m_mesh_update_thread; + private: void loadMods(); bool checkBuiltinIntegrity(); @@ -449,7 +457,6 @@ private: void ReceiveAll(); - void sendPlayerPos(); void deleteAuthData(); // helper method shared with clientpackethandler @@ -482,7 +489,6 @@ private: MtEventManager *m_event; - MeshUpdateThread m_mesh_update_thread; ClientEnvironment m_env; ParticleManager m_particle_manager; std::unique_ptr<con::Connection> m_con; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index ea7be4200..2b50fbf64 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -198,7 +198,7 @@ void ClientEnvironment::step(float dtime) lplayer->applyControl(dtime_part, this); // Apply physics - if (!free_move && !is_climbing) { + if (!free_move && !is_climbing && !g_settings->getBool("freecam")) { // Gravity v3f speed = lplayer->getSpeed(); if (!lplayer->in_liquid) diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index 864496a41..52d999c99 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -92,6 +92,11 @@ public: { return m_ao_manager.getActiveObject(id); } + + std::unordered_map<u16, ClientActiveObject*> getAllActiveObjects() + { + return m_ao_manager.getAllActiveObjects(); + } /* Adds an active object to the environment. diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 09072858a..294687ff8 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -587,7 +587,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) // - Do not if player is in third person mode const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; - if(features.solidness == 2 && !(g_settings->getBool("noclip") && + if(features.solidness == 2 && !((g_settings->getBool("noclip") || g_settings->getBool("freecam")) && m_client->checkLocalPrivilege("noclip")) && cam_mode == CAMERA_MODE_FIRST) { diff --git a/src/client/clientobject.h b/src/client/clientobject.h index ecd8059ef..4a1743d72 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "activeobject.h" +#include <ISceneNode.h> +#include <IMeshSceneNode.h> +#include <IAnimatedMeshSceneNode.h> +#include <IDummyTransformationSceneNode.h> +#include <IBillboardSceneNode.h> #include <unordered_map> #include <unordered_set> diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index c645900aa..7208212d4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <algorithm> #include <cmath> #include "client/shader.h" +#include "script/scripting_client.h" #include "client/minimap.h" class Settings; @@ -240,7 +241,7 @@ void TestCAO::addToScene(ITextureSource *tsrc) u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_LIGHTING, true); // false buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); buf->getMaterial().setTexture(0, tsrc->getTextureForMesh("rat.png")); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); @@ -478,11 +479,14 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, ClientActiveObject *parent = m_env->getActiveObject(parent_id); if (parent_id != old_parent) { + if (old_parent) + m_waiting_for_reattach = 10; if (auto *o = m_env->getActiveObject(old_parent)) o->removeAttachmentChild(m_id); if (parent) parent->addAttachmentChild(m_id); } + updateAttachments(); // Forcibly show attachments if required by set_attach @@ -827,13 +831,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc) } void GenericCAO::updateLight(u32 day_night_ratio) -{ +{ if (m_glow < 0) return; u8 light_at_pos = 0; bool pos_ok = false; - + v3s16 pos[3]; u16 npos = getLightPosition(pos); for (u16 i = 0; i < npos; i++) { @@ -849,6 +853,8 @@ void GenericCAO::updateLight(u32 day_night_ratio) light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); u8 light = decode_light(light_at_pos + m_glow); + if (g_settings->getBool("fullbright")) + light = 255; if (light != m_last_light) { m_last_light = light; setNodeLight(light); @@ -931,7 +937,7 @@ void GenericCAO::updateMarker() void GenericCAO::updateNametag() { - if (m_is_local_player) // No nametag for local player + if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player return; if (m_prop.nametag.empty()) { @@ -985,10 +991,12 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Handle model animations and update positions instantly to prevent lags if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); - m_position = player->getPosition(); + m_position = player->getLegitPosition(); pos_translator.val_current = m_position; - m_rotation.Y = wrapDegrees_0_360(player->getYaw()); - rot_translator.val_current = m_rotation; + if (! g_settings->getBool("freecam")) { + m_rotation.Y = wrapDegrees_0_360(player->getYaw()); + rot_translator.val_current = m_rotation; + } if (m_is_visible) { int old_anim = player->last_animation; @@ -998,9 +1006,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) const PlayerControl &controls = player->getPlayerControl(); bool walking = false; - if (controls.up || controls.down || controls.left || controls.right || + if ((controls.up || controls.down || controls.left || controls.right || controls.forw_move_joystick_axis != 0.f || - controls.sidew_move_joystick_axis != 0.f) + controls.sidew_move_joystick_axis != 0.f) && ! g_settings->getBool("freecam")) walking = true; f32 new_speed = player->local_animation_speed; @@ -1016,7 +1024,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_client->checkLocalPrivilege("fly")))) new_speed *= 1.5; // slowdown speed if sneeking - if (controls.sneak && walking) + if (controls.sneak && walking && ! g_settings->getBool("no_slow")) new_speed /= 2; if (walking && (controls.dig || controls.place)) { @@ -1741,6 +1749,11 @@ void GenericCAO::processMessage(const std::string &data) if(m_is_local_player) { + Client *client = m_env->getGameDef(); + + if (client->modsLoaded() && client->getScript()->on_recieve_physics_override(override_speed, override_jump, override_gravity, sneak, sneak_glitch, new_move)) + return; + LocalPlayer *player = m_env->getLocalPlayer(); player->physics_override_speed = override_speed; player->physics_override_jump = override_jump; diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 7c134fb48..09c26bd9c 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -172,6 +172,21 @@ public: inline const v3f &getRotation() const { return m_rotation; } + inline const v3f getAcceleration() const + { + return m_acceleration; + } + + inline const v3f getVelocity() const + { + return m_velocity; + } + + inline const u16 getHp() const + { + return m_hp; + } + const bool isImmortal(); scene::ISceneNode *getSceneNode() const; @@ -208,6 +223,16 @@ public: return m_is_local_player; } + inline std::string getName() const + { + return m_name; + } + + inline bool isPlayer() const + { + return m_is_player; + } + inline bool isVisible() const { return m_is_visible; @@ -228,6 +253,7 @@ public: void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); ClientActiveObject *getParent() const; + int getParentId() const { return m_attachment_parent_id; } const std::unordered_set<int> &getAttachmentChildIds() const { return m_attachment_child_ids; } void updateAttachments(); @@ -281,6 +307,13 @@ public: { return m_prop.infotext; } + + float m_waiting_for_reattach; + + ObjectProperties *getProperties() + { + return &m_prop; + } void updateMeshCulling(); }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 2001f0487..cc2a1bc43 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -77,850 +77,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #else #include "client/sound.h" #endif -/* - Text input system -*/ - -struct TextDestNodeMetadata : public TextDest -{ - TextDestNodeMetadata(v3s16 p, Client *client) - { - m_p = p; - m_client = client; - } - // This is deprecated I guess? -celeron55 - void gotText(const std::wstring &text) - { - std::string ntext = wide_to_utf8(text); - infostream << "Submitting 'text' field of node at (" << m_p.X << "," - << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; - StringMap fields; - fields["text"] = ntext; - m_client->sendNodemetaFields(m_p, "", fields); - } - void gotText(const StringMap &fields) - { - m_client->sendNodemetaFields(m_p, "", fields); - } - - v3s16 m_p; - Client *m_client; -}; - -struct TextDestPlayerInventory : public TextDest -{ - TextDestPlayerInventory(Client *client) - { - m_client = client; - m_formname = ""; - } - TextDestPlayerInventory(Client *client, const std::string &formname) - { - m_client = client; - m_formname = formname; - } - void gotText(const StringMap &fields) - { - m_client->sendInventoryFields(m_formname, fields); - } - - Client *m_client; -}; - -struct LocalFormspecHandler : public TextDest -{ - LocalFormspecHandler(const std::string &formname) - { - m_formname = formname; - } - - LocalFormspecHandler(const std::string &formname, Client *client): - m_client(client) - { - m_formname = formname; - } - - void gotText(const StringMap &fields) - { - if (m_formname == "MT_PAUSE_MENU") { - if (fields.find("btn_sound") != fields.end()) { - g_gamecallback->changeVolume(); - return; - } - - if (fields.find("btn_key_config") != fields.end()) { - g_gamecallback->keyConfig(); - return; - } - - if (fields.find("btn_exit_menu") != fields.end()) { - g_gamecallback->disconnect(); - return; - } - - if (fields.find("btn_exit_os") != fields.end()) { - g_gamecallback->exitToOS(); -#ifndef __ANDROID__ - RenderingEngine::get_raw_device()->closeDevice(); -#endif - return; - } - - if (fields.find("btn_change_password") != fields.end()) { - g_gamecallback->changePassword(); - return; - } - - if (fields.find("quit") != fields.end()) { - return; - } - - if (fields.find("btn_continue") != fields.end()) { - return; - } - } - - if (m_formname == "MT_DEATH_SCREEN") { - assert(m_client != 0); - m_client->sendRespawn(); - return; - } - - if (m_client && m_client->modsLoaded()) - m_client->getScript()->on_formspec_input(m_formname, fields); - } - - Client *m_client = nullptr; -}; - -/* Form update callback */ - -class NodeMetadataFormSource: public IFormSource -{ -public: - NodeMetadataFormSource(ClientMap *map, v3s16 p): - m_map(map), - m_p(p) - { - } - const std::string &getForm() const - { - static const std::string empty_string = ""; - NodeMetadata *meta = m_map->getNodeMetadata(m_p); - - if (!meta) - return empty_string; - - return meta->getString("formspec"); - } - - virtual std::string resolveText(const std::string &str) - { - NodeMetadata *meta = m_map->getNodeMetadata(m_p); - - if (!meta) - return str; - - return meta->resolveString(str); - } - - ClientMap *m_map; - v3s16 m_p; -}; - -class PlayerInventoryFormSource: public IFormSource -{ -public: - PlayerInventoryFormSource(Client *client): - m_client(client) - { - } - - const std::string &getForm() const - { - LocalPlayer *player = m_client->getEnv().getLocalPlayer(); - return player->inventory_formspec; - } - - Client *m_client; -}; - -class NodeDugEvent: public MtEvent -{ -public: - v3s16 p; - MapNode n; - - NodeDugEvent(v3s16 p, MapNode n): - p(p), - n(n) - {} - MtEvent::Type getType() const - { - return MtEvent::NODE_DUG; - } -}; - -class SoundMaker -{ - ISoundManager *m_sound; - const NodeDefManager *m_ndef; -public: - bool makes_footstep_sound; - float m_player_step_timer; - float m_player_jump_timer; - - SimpleSoundSpec m_player_step_sound; - SimpleSoundSpec m_player_leftpunch_sound; - SimpleSoundSpec m_player_rightpunch_sound; - - SoundMaker(ISoundManager *sound, const NodeDefManager *ndef): - m_sound(sound), - m_ndef(ndef), - makes_footstep_sound(true), - m_player_step_timer(0.0f), - m_player_jump_timer(0.0f) - { - } - - void playPlayerStep() - { - if (m_player_step_timer <= 0 && m_player_step_sound.exists()) { - m_player_step_timer = 0.03; - if (makes_footstep_sound) - m_sound->playSound(m_player_step_sound, false); - } - } - - void playPlayerJump() - { - if (m_player_jump_timer <= 0.0f) { - m_player_jump_timer = 0.2f; - m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false); - } - } - - static void viewBobbingStep(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->playPlayerStep(); - } - - static void playerRegainGround(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->playPlayerStep(); - } - - static void playerJump(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->playPlayerJump(); - } - - static void cameraPunchLeft(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->m_sound->playSound(sm->m_player_leftpunch_sound, false); - } - - static void cameraPunchRight(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->m_sound->playSound(sm->m_player_rightpunch_sound, false); - } - - static void nodeDug(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - NodeDugEvent *nde = (NodeDugEvent *)e; - sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false); - } - - static void playerDamage(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false); - } - - static void playerFallingDamage(MtEvent *e, void *data) - { - SoundMaker *sm = (SoundMaker *)data; - sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false); - } - - void registerReceiver(MtEventManager *mgr) - { - mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this); - mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this); - mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this); - mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this); - mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this); - mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this); - mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this); - mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this); - } - - void step(float dtime) - { - m_player_step_timer -= dtime; - m_player_jump_timer -= dtime; - } -}; - -// Locally stored sounds don't need to be preloaded because of this -class GameOnDemandSoundFetcher: public OnDemandSoundFetcher -{ - std::set<std::string> m_fetched; -private: - void paths_insert(std::set<std::string> &dst_paths, - const std::string &base, - const std::string &name) - { - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg"); - dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg"); - } -public: - void fetchSounds(const std::string &name, - std::set<std::string> &dst_paths, - std::set<std::string> &dst_datas) - { - if (m_fetched.count(name)) - return; - - m_fetched.insert(name); - - paths_insert(dst_paths, porting::path_share, name); - paths_insert(dst_paths, porting::path_user, name); - } -}; - - -// before 1.8 there isn't a "integer interface", only float -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) -typedef f32 SamplerLayer_t; -#else -typedef s32 SamplerLayer_t; -#endif - - -class GameGlobalShaderConstantSetter : public IShaderConstantSetter -{ - Sky *m_sky; - bool *m_force_fog_off; - f32 *m_fog_range; - bool m_fog_enabled; - CachedPixelShaderSetting<float, 4> m_sky_bg_color; - CachedPixelShaderSetting<float> m_fog_distance; - CachedVertexShaderSetting<float> m_animation_timer_vertex; - CachedPixelShaderSetting<float> m_animation_timer_pixel; - CachedPixelShaderSetting<float, 3> m_day_light; - CachedPixelShaderSetting<float, 4> m_star_color; - CachedPixelShaderSetting<float, 3> m_eye_position_pixel; - CachedVertexShaderSetting<float, 3> m_eye_position_vertex; - CachedPixelShaderSetting<float, 3> m_minimap_yaw; - CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; - CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; - CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; - Client *m_client; - -public: - void onSettingsChange(const std::string &name) - { - if (name == "enable_fog") - m_fog_enabled = g_settings->getBool("enable_fog"); - } - - static void settingsCallback(const std::string &name, void *userdata) - { - reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name); - } - - void setSky(Sky *sky) { m_sky = sky; } - - GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off, - f32 *fog_range, Client *client) : - m_sky(sky), - m_force_fog_off(force_fog_off), - m_fog_range(fog_range), - m_sky_bg_color("skyBgColor"), - m_fog_distance("fogDistance"), - m_animation_timer_vertex("animationTimer"), - m_animation_timer_pixel("animationTimer"), - m_day_light("dayLight"), - m_star_color("starColor"), - m_eye_position_pixel("eyePosition"), - m_eye_position_vertex("eyePosition"), - m_minimap_yaw("yawVec"), - m_camera_offset_pixel("cameraOffset"), - m_camera_offset_vertex("cameraOffset"), - m_base_texture("baseTexture"), - m_client(client) - { - g_settings->registerChangedCallback("enable_fog", settingsCallback, this); - m_fog_enabled = g_settings->getBool("enable_fog"); - } - - ~GameGlobalShaderConstantSetter() - { - g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this); - } - - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) - { - if (!is_highlevel) - return; - - // Background color - video::SColor bgcolor = m_sky->getBgColor(); - video::SColorf bgcolorf(bgcolor); - float bgcolorfa[4] = { - bgcolorf.r, - bgcolorf.g, - bgcolorf.b, - bgcolorf.a, - }; - m_sky_bg_color.set(bgcolorfa, services); - - // Fog distance - float fog_distance = 10000 * BS; - - if (m_fog_enabled && !*m_force_fog_off) - fog_distance = *m_fog_range; - - m_fog_distance.set(&fog_distance, services); - - u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio(); - video::SColorf sunlight; - get_sunlight_color(&sunlight, daynight_ratio); - float dnc[3] = { - sunlight.r, - sunlight.g, - sunlight.b }; - m_day_light.set(dnc, services); - - video::SColorf star_color = m_sky->getCurrentStarColor(); - float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a}; - m_star_color.set(clr, services); - - u32 animation_timer = porting::getTimeMs() % 1000000; - float animation_timer_f = (float)animation_timer / 100000.f; - m_animation_timer_vertex.set(&animation_timer_f, services); - m_animation_timer_pixel.set(&animation_timer_f, services); - - float eye_position_array[3]; - v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - eye_position_array[0] = epos.X; - eye_position_array[1] = epos.Y; - eye_position_array[2] = epos.Z; -#else - epos.getAs3Values(eye_position_array); -#endif - m_eye_position_pixel.set(eye_position_array, services); - m_eye_position_vertex.set(eye_position_array, services); - - if (m_client->getMinimap()) { - float minimap_yaw_array[3]; - v3f minimap_yaw = m_client->getMinimap()->getYawVec(); -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - minimap_yaw_array[0] = minimap_yaw.X; - minimap_yaw_array[1] = minimap_yaw.Y; - minimap_yaw_array[2] = minimap_yaw.Z; -#else - minimap_yaw.getAs3Values(minimap_yaw_array); -#endif - m_minimap_yaw.set(minimap_yaw_array, services); - } - - float camera_offset_array[3]; - v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); -#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - camera_offset_array[0] = offset.X; - camera_offset_array[1] = offset.Y; - camera_offset_array[2] = offset.Z; -#else - offset.getAs3Values(camera_offset_array); -#endif - m_camera_offset_pixel.set(camera_offset_array, services); - m_camera_offset_vertex.set(camera_offset_array, services); - - SamplerLayer_t base_tex = 0; - m_base_texture.set(&base_tex, services); - } -}; - - -class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory -{ - Sky *m_sky; - bool *m_force_fog_off; - f32 *m_fog_range; - Client *m_client; - std::vector<GameGlobalShaderConstantSetter *> created_nosky; -public: - GameGlobalShaderConstantSetterFactory(bool *force_fog_off, - f32 *fog_range, Client *client) : - m_sky(NULL), - m_force_fog_off(force_fog_off), - m_fog_range(fog_range), - m_client(client) - {} - - void setSky(Sky *sky) { - m_sky = sky; - for (GameGlobalShaderConstantSetter *ggscs : created_nosky) { - ggscs->setSky(m_sky); - } - created_nosky.clear(); - } - - virtual IShaderConstantSetter* create() - { - GameGlobalShaderConstantSetter *scs = new GameGlobalShaderConstantSetter( - m_sky, m_force_fog_off, m_fog_range, m_client); - if (!m_sky) - created_nosky.push_back(scs); - return scs; - } -}; - -#ifdef __ANDROID__ -#define SIZE_TAG "size[11,5.5]" -#else -#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop -#endif - -/**************************************************************************** - ****************************************************************************/ - -const float object_hit_delay = 0.2; - -struct FpsControl { - u32 last_time, busy_time, sleep_time; -}; - - -/* The reason the following structs are not anonymous structs within the - * class is that they are not used by the majority of member functions and - * many functions that do require objects of thse types do not modify them - * (so they can be passed as a const qualified parameter) - */ - -struct GameRunData { - u16 dig_index; - u16 new_playeritem; - PointedThing pointed_old; - bool digging; - bool punching; - bool btn_down_for_dig; - bool dig_instantly; - bool digging_blocked; - bool reset_jump_timer; - float nodig_delay_timer; - float dig_time; - float dig_time_complete; - float repeat_place_timer; - float object_hit_delay_timer; - float time_from_last_punch; - ClientActiveObject *selected_object; - - float jump_timer; - float damage_flash; - float update_draw_list_timer; - - f32 fog_range; - - v3f update_draw_list_last_cam_dir; - - float time_of_day_smooth; -}; - -class Game; - -struct ClientEventHandler -{ - void (Game::*handler)(ClientEvent *, CameraOrientation *); -}; - -/**************************************************************************** - THE GAME - ****************************************************************************/ - -/* This is not intended to be a public class. If a public class becomes - * desirable then it may be better to create another 'wrapper' class that - * hides most of the stuff in this class (nothing in this class is required - * by any other file) but exposes the public methods/data only. - */ -class Game { -public: - Game(); - ~Game(); - - bool startup(bool *kill, - InputHandler *input, - const GameStartData &game_params, - std::string &error_message, - bool *reconnect, - ChatBackend *chat_backend); - - void run(); - void shutdown(); - -protected: - - void extendedResourceCleanup(); - - // Basic initialisation - bool init(const std::string &map_dir, const std::string &address, - u16 port, const SubgameSpec &gamespec); - bool initSound(); - bool createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port); - - // Client creation - bool createClient(const GameStartData &start_data); - bool initGui(); - - // Client connection - bool connectToServer(const GameStartData &start_data, - bool *connect_ok, bool *aborted); - bool getServerContent(bool *aborted); - - // Main loop - - void updateInteractTimers(f32 dtime); - bool checkConnection(); - bool handleCallbacks(); - void processQueues(); - void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime); - void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); - void updateProfilerGraphs(ProfilerGraph *graph); - - // Input related - void processUserInput(f32 dtime); - void processKeyInput(); - void processItemSelection(u16 *new_playeritem); - - void dropSelectedItem(bool single_item = false); - void openInventory(); - void openConsole(float scale, const wchar_t *line=NULL); - void toggleFreeMove(); - void toggleFreeMoveAlt(); - void togglePitchMove(); - void toggleFast(); - void toggleNoClip(); - void toggleCinematic(); - void toggleAutoforward(); - - void toggleMinimap(bool shift_pressed); - void toggleFog(); - void toggleDebug(); - void toggleUpdateCamera(); - - void increaseViewRange(); - void decreaseViewRange(); - void toggleFullViewRange(); - void checkZoomEnabled(); - - void updateCameraDirection(CameraOrientation *cam, float dtime); - void updateCameraOrientation(CameraOrientation *cam, float dtime); - void updatePlayerControl(const CameraOrientation &cam); - void step(f32 *dtime); - void processClientEvents(CameraOrientation *cam); - void updateCamera(u32 busy_time, f32 dtime); - void updateSound(f32 dtime); - void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug); - /*! - * Returns the object or node the player is pointing at. - * Also updates the selected thing in the Hud. - * - * @param[in] shootline the shootline, starting from - * the camera position. This also gives the maximal distance - * of the search. - * @param[in] liquids_pointable if false, liquids are ignored - * @param[in] look_for_object if false, objects are ignored - * @param[in] camera_offset offset of the camera - * @param[out] selected_object the selected object or - * NULL if not found - */ - PointedThing updatePointedThing( - const core::line3d<f32> &shootline, bool liquids_pointable, - bool look_for_object, const v3s16 &camera_offset); - void handlePointingAtNothing(const ItemStack &playerItem); - void handlePointingAtNode(const PointedThing &pointed, - const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); - void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, - const v3f &player_position, bool show_debug); - void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); - void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, - const CameraOrientation &cam); - - // Misc - void limitFps(FpsControl *fps_timings, f32 *dtime); - - void showOverlayMessage(const char *msg, float dtime, int percent, - bool draw_clouds = true); - - static void settingChangedCallback(const std::string &setting_name, void *data); - void readSettings(); - - inline bool isKeyDown(GameKeyType k) - { - return input->isKeyDown(k); - } - inline bool wasKeyDown(GameKeyType k) - { - return input->wasKeyDown(k); - } - inline bool wasKeyPressed(GameKeyType k) - { - return input->wasKeyPressed(k); - } - inline bool wasKeyReleased(GameKeyType k) - { - return input->wasKeyReleased(k); - } - -#ifdef __ANDROID__ - void handleAndroidChatInput(); -#endif - -private: - struct Flags { - bool force_fog_off = false; - bool disable_camera_update = false; - }; - - void showDeathFormspec(); - void showPauseMenu(); - - // ClientEvent handlers - void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_HandleParticleEvent(ClientEvent *event, - CameraOrientation *cam); - void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, - CameraOrientation *cam); - void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam); - - void updateChat(f32 dtime, const v2u32 &screensize); - - bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, - const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, - const NodeMetadata *meta); - static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; - - InputHandler *input = nullptr; - - Client *client = nullptr; - Server *server = nullptr; - - IWritableTextureSource *texture_src = nullptr; - IWritableShaderSource *shader_src = nullptr; - - // When created, these will be filled with data received from the server - IWritableItemDefManager *itemdef_manager = nullptr; - NodeDefManager *nodedef_manager = nullptr; - - GameOnDemandSoundFetcher soundfetcher; // useful when testing - ISoundManager *sound = nullptr; - bool sound_is_dummy = false; - SoundMaker *soundmaker = nullptr; - - ChatBackend *chat_backend = nullptr; - LogOutputBuffer m_chat_log_buf; - - EventManager *eventmgr = nullptr; - QuicktuneShortcutter *quicktune = nullptr; - bool registration_confirmation_shown = false; - - std::unique_ptr<GameUI> m_game_ui; - GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop() - MapDrawControl *draw_control = nullptr; - Camera *camera = nullptr; - Clouds *clouds = nullptr; // Free using ->Drop() - Sky *sky = nullptr; // Free using ->Drop() - Hud *hud = nullptr; - Minimap *mapper = nullptr; - - GameRunData runData; - Flags m_flags; - - /* 'cache' - This class does take ownership/responsibily for cleaning up etc of any of - these items (e.g. device) - */ - IrrlichtDevice *device; - video::IVideoDriver *driver; - scene::ISceneManager *smgr; - bool *kill; - std::string *error_message; - bool *reconnect_requested; - scene::ISceneNode *skybox; - - bool simple_singleplayer_mode; - /* End 'cache' */ - - /* Pre-calculated values - */ - int crack_animation_length; - - IntervalLimiter profiler_interval; - - /* - * TODO: Local caching of settings is not optimal and should at some stage - * be updated to use a global settings object for getting thse values - * (as opposed to the this local caching). This can be addressed in - * a later release. - */ - bool m_cache_doubletap_jump; - bool m_cache_enable_clouds; - bool m_cache_enable_joysticks; - bool m_cache_enable_particles; - bool m_cache_enable_fog; - bool m_cache_enable_noclip; - bool m_cache_enable_free_move; - f32 m_cache_mouse_sensitivity; - f32 m_cache_joystick_frustum_sensitivity; - f32 m_repeat_place_time; - f32 m_cache_cam_smoothing; - f32 m_cache_fog_start; - - bool m_invert_mouse = false; - bool m_first_loop_after_window_activation = false; - bool m_camera_offset_changed = false; - - bool m_does_lost_focus_pause_game = false; - - int m_reset_HW_buffer_counter = 0; -#ifdef __ANDROID__ - bool m_cache_hold_aux1; - bool m_android_chat_open; -#endif -}; Game::Game() : m_chat_log_buf(g_logger), @@ -952,7 +108,17 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("camera_smoothing", &settingChangedCallback, this); - + g_settings->registerChangedCallback("freecam", + &freecamChangedCallback, this); + g_settings->registerChangedCallback("xray", + &updateAllMapBlocksCallback, this); + g_settings->registerChangedCallback("xray_nodes", + &updateAllMapBlocksCallback, this); + g_settings->registerChangedCallback("fullbright", + &updateAllMapBlocksCallback, this); + g_settings->registerChangedCallback("node_esp_nodes", + &updateAllMapBlocksCallback, this); + readSettings(); #ifdef __ANDROID__ @@ -1010,6 +176,16 @@ Game::~Game() &settingChangedCallback, this); g_settings->deregisterChangedCallback("camera_smoothing", &settingChangedCallback, this); + g_settings->deregisterChangedCallback("freecam", + &freecamChangedCallback, this); + g_settings->deregisterChangedCallback("xray", + &updateAllMapBlocksCallback, this); + g_settings->deregisterChangedCallback("xray_nodes", + &updateAllMapBlocksCallback, this); + g_settings->deregisterChangedCallback("fullbright", + &updateAllMapBlocksCallback, this); + g_settings->deregisterChangedCallback("node_esp_nodes", + &updateAllMapBlocksCallback, this); } bool Game::startup(bool *kill, @@ -1066,11 +242,9 @@ void Game::run() { ProfilerGraph graph; RunStats stats = { 0 }; - CameraOrientation cam_view_target = { 0 }; - CameraOrientation cam_view = { 0 }; FpsControl draw_times = { 0 }; f32 dtime; // in seconds - + /* Clear the profiler */ Profiler::GraphValues dummyvalues; g_profiler->graphGet(dummyvalues); @@ -1176,6 +350,9 @@ void Game::shutdown() if (gui_chat_console) gui_chat_console->drop(); + if (m_cheat_menu) + delete m_cheat_menu; + if (sky) sky->drop(); @@ -1398,7 +575,7 @@ bool Game::createClient(const GameStartData &start_data) str += L" "; str += utf8_to_wide(g_version_hash); str += L" ["; - str += driver->getName(); + str += L"Minetest Hackclient"; str += L"]"; device->setWindowCaption(str.c_str()); @@ -1435,11 +612,20 @@ bool Game::initGui() // Chat backend and console gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); + if (!gui_chat_console) { *error_message = "Could not allocate memory for chat console"; errorstream << *error_message << std::endl; return false; } + + m_cheat_menu = new CheatMenu(client); + + if (!m_cheat_menu) { + *error_message = "Could not allocate memory for cheat menu"; + errorstream << *error_message << std::endl; + return false; + } #ifdef HAVE_TOUCHSCREENGUI @@ -1882,6 +1068,18 @@ void Game::processUserInput(f32 dtime) void Game::processKeyInput() { + if (wasKeyDown(KeyType::SELECT_UP)) { + m_cheat_menu->selectUp(); + } else if (wasKeyDown(KeyType::SELECT_DOWN)) { + m_cheat_menu->selectDown(); + } else if (wasKeyDown(KeyType::SELECT_LEFT)) { + m_cheat_menu->selectLeft(); + } else if (wasKeyDown(KeyType::SELECT_RIGHT)) { + m_cheat_menu->selectRight(); + } else if (wasKeyDown(KeyType::SELECT_CONFIRM)) { + m_cheat_menu->selectConfirm(); + } + if (wasKeyDown(KeyType::DROP)) { dropSelectedItem(isKeyDown(KeyType::SNEAK)); } else if (wasKeyDown(KeyType::AUTOFORWARD)) { @@ -1891,6 +1089,8 @@ void Game::processKeyInput() toggleAutoforward(); } else if (wasKeyDown(KeyType::INVENTORY)) { openInventory(); + } else if (wasKeyDown(KeyType::ENDERCHEST)) { + openEnderchest(); } else if (input->cancelPressed()) { #ifdef __ANDROID__ m_android_chat_open = false; @@ -1919,6 +1119,14 @@ void Game::processKeyInput() toggleFast(); } else if (wasKeyDown(KeyType::NOCLIP)) { toggleNoClip(); + } else if (wasKeyDown(KeyType::KILLAURA)) { + toggleKillaura(); + } else if (wasKeyDown(KeyType::FREECAM)) { + toggleFreecam(); + } else if (wasKeyDown(KeyType::SCAFFOLD)) { + toggleScaffold(); + } else if (wasKeyDown(KeyType::NEXT_ITEM)) { + toggleNextItem(); #if USE_SOUND } else if (wasKeyDown(KeyType::MUTE)) { if (g_settings->getBool("enable_sound")) { @@ -1972,6 +1180,8 @@ void Game::processKeyInput() m_game_ui->toggleChat(); } else if (wasKeyDown(KeyType::TOGGLE_FOG)) { toggleFog(); + } else if (wasKeyDown(KeyType::TOGGLE_CHEAT_MENU)) { + m_game_ui->toggleCheatMenu(); } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) { toggleUpdateCamera(); } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) { @@ -2083,6 +1293,18 @@ void Game::openInventory() } } +void Game::openEnderchest() +{ + LocalPlayer *player = client->getEnv().getLocalPlayer(); + if (!player || !player->getCAO()) + return; + + infostream << "Game: Launching special inventory" << std::endl; + + if (client->modsLoaded()) + client->getScript()->open_enderchest(); +} + void Game::openConsole(float scale, const wchar_t *line) { @@ -2190,6 +1412,54 @@ void Game::toggleNoClip() } } +void Game::toggleKillaura() +{ + bool killaura = ! g_settings->getBool("killaura"); + g_settings->set("killaura", bool_to_cstr(killaura)); + + if (killaura) { + m_game_ui->showTranslatedStatusText("Killaura enabled"); + } else { + m_game_ui->showTranslatedStatusText("Killaura disabled"); + } +} + +void Game::toggleFreecam() +{ + bool freecam = ! g_settings->getBool("freecam"); + g_settings->set("freecam", bool_to_cstr(freecam)); + + if (freecam) { + m_game_ui->showTranslatedStatusText("Freecam enabled"); + } else { + m_game_ui->showTranslatedStatusText("Freecam disabled"); + } +} + +void Game::toggleScaffold() +{ + bool scaffold = ! g_settings->getBool("scaffold"); + g_settings->set("scaffold", bool_to_cstr(scaffold)); + + if (scaffold) { + m_game_ui->showTranslatedStatusText("Scaffold enabled"); + } else { + m_game_ui->showTranslatedStatusText("Scaffold disabled"); + } +} + +void Game::toggleNextItem() +{ + bool next_item = ! g_settings->getBool("next_item"); + g_settings->set("next_item", bool_to_cstr(next_item)); + + if (next_item) { + m_game_ui->showTranslatedStatusText("NextItem enabled"); + } else { + m_game_ui->showTranslatedStatusText("NextItem disabled"); + } +} + void Game::toggleCinematic() { bool cinematic = !g_settings->getBool("cinematic"); @@ -2295,6 +1565,8 @@ void Game::toggleDebug() void Game::toggleUpdateCamera() { + if (g_settings->getBool("freecam")) + return; m_flags.disable_camera_update = !m_flags.disable_camera_update; if (m_flags.disable_camera_update) m_game_ui->showTranslatedStatusText("Camera update disabled"); @@ -2922,18 +2194,9 @@ void Game::updateCamera(u32 busy_time, f32 dtime) v3s16 old_camera_offset = camera->getOffset(); - if (wasKeyDown(KeyType::CAMERA_MODE)) { - GenericCAO *playercao = player->getCAO(); - - // If playercao not loaded, don't change camera - if (!playercao) - return; - + if (wasKeyDown(KeyType::CAMERA_MODE) && ! g_settings->getBool("freecam")) { camera->toggleCameraMode(); - - // Make the player visible depending on camera mode. - playercao->updateMeshCulling(); - playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + updatePlayerCAOVisibility(); } float full_punch_interval = playeritem_toolcap.full_punch_interval; @@ -2964,6 +2227,16 @@ void Game::updateCamera(u32 busy_time, f32 dtime) } } +void Game::updatePlayerCAOVisibility() +{ + // Make the player visible depending on camera mode. + LocalPlayer *player = client->getEnv().getLocalPlayer(); + GenericCAO *playercao = player->getCAO(); + if (!playercao) + return; + playercao->updateMeshCulling(); + playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam")); +} void Game::updateSound(f32 dtime) { @@ -3019,6 +2292,11 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager)); + + if (g_settings->getBool("increase_tool_range")) + d += 2; + if (g_settings->getBool("increase_tool_range_plus")) + d = 1000; core::line3d<f32> shootline; @@ -3108,7 +2386,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) soundmaker->m_player_leftpunch_sound.name = ""; // Prepare for repeating, unless we're not supposed to - if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place")) + if ((isKeyDown(KeyType::PLACE) || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place")) runData.repeat_place_timer += dtime; else runData.repeat_place_timer = 0; @@ -3166,8 +2444,7 @@ PointedThing Game::updatePointedThing( runData.selected_object = NULL; hud->pointing_at_object = false; - - RaycastState s(shootline, look_for_object, liquids_pointable); + RaycastState s(shootline, look_for_object, liquids_pointable, ! g_settings->getBool("dont_point_nodes")); PointedThing result; env.continueRaycast(&s, &result); if (result.type == POINTEDTHING_OBJECT) { @@ -3241,7 +2518,6 @@ PointedThing Game::updatePointedThing( return result; } - void Game::handlePointingAtNothing(const ItemStack &playerItem) { infostream << "Attempted to place item while pointing at nothing" << std::endl; @@ -3263,9 +2539,10 @@ void Game::handlePointingAtNode(const PointedThing &pointed, ClientMap &map = client->getEnv().getClientMap(); - if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG) + if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (isKeyDown(KeyType::DIG) || g_settings->getBool("autodig")) && !runData.digging_blocked - && client->checkPrivilege("interact")) { + && client->checkPrivilege("interact")) + ) { handleDigging(pointed, nodepos, selected_item, hand_item, dtime); } @@ -3284,8 +2561,8 @@ void Game::handlePointingAtNode(const PointedThing &pointed, } } - if ((wasKeyPressed(KeyType::PLACE) || - runData.repeat_place_timer >= m_repeat_place_time) && + if (((wasKeyPressed(KeyType::PLACE) || g_settings->getBool("autoplace")) || + (runData.repeat_place_timer >= (g_settings->getBool("fastplace") ? 0 : m_repeat_place_time))) && client->checkPrivilege("interact")) { runData.repeat_place_timer = 0; infostream << "Place button pressed while looking at ground" << std::endl; @@ -3518,11 +2795,11 @@ void Game::handlePointingAtObject(const PointedThing &pointed, m_game_ui->setInfoText(infotext); - if (isKeyDown(KeyType::DIG)) { + if (isKeyDown(KeyType::DIG) || g_settings->getBool("autohit")) { bool do_punch = false; bool do_punch_damage = false; - if (runData.object_hit_delay_timer <= 0.0) { + if (runData.object_hit_delay_timer <= 0.0 || g_settings->getBool("spamclick")) { do_punch = true; do_punch_damage = true; runData.object_hit_delay_timer = object_hit_delay; @@ -3545,8 +2822,9 @@ void Game::handlePointingAtObject(const PointedThing &pointed, dir, &tool_item, runData.time_from_last_punch); runData.time_from_last_punch = 0; - if (!disable_send) + if (!disable_send) { client->interact(INTERACT_START_DIGGING, pointed); + } } } else if (wasKeyDown(KeyType::PLACE)) { infostream << "Pressed place button while pointing at object" << std::endl; @@ -3587,7 +2865,11 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, player, nodepos, n, features); } } - + + if(g_settings->getBool("instant_break")) { + runData.dig_time_complete = 0; + runData.dig_instantly = true; + } if (!runData.digging) { infostream << "Started digging" << std::endl; runData.dig_instantly = runData.dig_time_complete == 0; @@ -3901,7 +3183,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, } #endif RenderingEngine::draw_scene(skycolor, m_game_ui->m_flags.show_hud, - m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair); + m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair, g_settings->getBool("enable_esp"), g_settings->getBool("enable_tracers"), g_settings->getBool("enable_node_esp"), g_settings->getBool("enable_node_tracers")); /* Profiler graph @@ -3910,13 +3192,22 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont()); /* + Cheat menu + */ + + if (! gui_chat_console->isOpen()) { + if (m_game_ui->m_flags.show_cheat_menu) + m_cheat_menu->draw(driver, m_game_ui->m_flags.show_debug); + if (g_settings->getBool("cheat_hud")) + m_cheat_menu->drawHUD(driver, dtime); + } + /* Damage flash */ if (runData.damage_flash > 0.0f) { video::SColor color(runData.damage_flash, 180, 0, 0); - driver->draw2DRectangle(color, - core::rect<s32>(0, 0, screensize.X, screensize.Y), - NULL); + if (! g_settings->getBool("no_hurt_cam")) + driver->draw2DRectangle(color, core::rect<s32>(0, 0, screensize.X, screensize.Y), NULL); runData.damage_flash -= 384.0f * dtime; } @@ -3927,7 +3218,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, if (player->hurt_tilt_timer > 0.0f) { player->hurt_tilt_timer -= dtime * 6.0f; - if (player->hurt_tilt_timer < 0.0f) + if (player->hurt_tilt_timer < 0.0f || g_settings->getBool("no_hurt_cam")) player->hurt_tilt_strength = 0.0f; } @@ -4040,6 +3331,24 @@ void Game::settingChangedCallback(const std::string &setting_name, void *data) ((Game *)data)->readSettings(); } +void Game::updateAllMapBlocksCallback(const std::string &setting_name, void *data) +{ + ((Game *) data)->client->updateAllMapBlocks(); +} + +void Game::freecamChangedCallback(const std::string &setting_name, void *data) +{ + Game *game = (Game *) data; + LocalPlayer *player = game->client->getEnv().getLocalPlayer(); + if (g_settings->getBool("freecam")) { + game->camera->setCameraMode(CAMERA_MODE_FIRST); + player->freecamEnable(); + } else { + player->freecamDisable(); + } + game->updatePlayerCAOVisibility(); +} + void Game::readSettings() { m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); @@ -4147,26 +3456,36 @@ void Game::showPauseMenu() "- %s: sneak/climb down\n" "- %s: drop item\n" "- %s: inventory\n" + "- %s: enderchest\n" "- Mouse: turn/look\n" "- Mouse wheel: select item\n" "- %s: chat\n" + "- %s: Killaura\n" + "- %s: Freecam\n" + "- %s: Scaffold\n" + "- %s: NextItem\n" ); - char control_text_buf[600]; - - porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), - GET_KEY_NAME(keymap_forward), - GET_KEY_NAME(keymap_backward), - GET_KEY_NAME(keymap_left), - GET_KEY_NAME(keymap_right), - GET_KEY_NAME(keymap_jump), - GET_KEY_NAME(keymap_dig), - GET_KEY_NAME(keymap_place), - GET_KEY_NAME(keymap_sneak), - GET_KEY_NAME(keymap_drop), - GET_KEY_NAME(keymap_inventory), - GET_KEY_NAME(keymap_chat) - ); + char control_text_buf[600]; + + porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), + GET_KEY_NAME(keymap_forward), + GET_KEY_NAME(keymap_backward), + GET_KEY_NAME(keymap_left), + GET_KEY_NAME(keymap_right), + GET_KEY_NAME(keymap_jump), + GET_KEY_NAME(keymap_dig), + GET_KEY_NAME(keymap_place), + GET_KEY_NAME(keymap_sneak), + GET_KEY_NAME(keymap_drop), + GET_KEY_NAME(keymap_inventory), + GET_KEY_NAME(keymap_enderchest), + GET_KEY_NAME(keymap_chat), + GET_KEY_NAME(keymap_toggle_killaura), + GET_KEY_NAME(keymap_toggle_freecam), + GET_KEY_NAME(keymap_toggle_scaffold), + GET_KEY_NAME(keymap_toggle_next_item) + ); std::string control_text = std::string(control_text_buf); str_formspec_escape(control_text); @@ -4259,6 +3578,8 @@ void Game::showPauseMenu() ****************************************************************************/ /****************************************************************************/ +Game *g_game; + void the_game(bool *kill, InputHandler *input, const GameStartData &start_data, @@ -4267,6 +3588,8 @@ void the_game(bool *kill, bool *reconnect_requested) // Used for local game { Game game; + + g_game = &game; /* Make a copy of the server address because if a local singleplayer server * is created then this is updated and we don't want to change the value diff --git a/src/client/game.h b/src/client/game.h index d04153271..26117ab86 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -19,6 +19,58 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include <iomanip> +#include <cmath> +#include "client/renderingengine.h" +#include "camera.h" +#include "client.h" +#include "client/clientevent.h" +//#include "client/gameui.h" +#include "client/inputhandler.h" +#include "client/sound.h" +#include "client/tile.h" // For TextureSource +#include "client/keys.h" +#include "client/joystick_controller.h" +#include "clientmap.h" +#include "clouds.h" +#include "config.h" +#include "content_cao.h" +#include "client/event_manager.h" +#include "fontengine.h" +#include "itemdef.h" +#include "log.h" +#include "filesys.h" +#include "gettext.h" +#include "gui/cheatMenu.h" +#include "gui/guiChatConsole.h" +#include "gui/guiConfirmRegistration.h" +#include "gui/guiFormSpecMenu.h" +#include "gui/guiKeyChangeMenu.h" +#include "gui/guiPasswordChange.h" +#include "gui/guiVolumeChange.h" +#include "gui/mainmenumanager.h" +#include "gui/profilergraph.h" +#include "mapblock.h" +#include "minimap.h" +#include "nodedef.h" // Needed for determining pointing to nodes +#include "nodemetadata.h" +#include "particles.h" +#include "porting.h" +#include "profiler.h" +#include "raycast.h" +#include "server.h" +#include "settings.h" +#include "shader.h" +#include "sky.h" +#include "translation.h" +#include "util/basic_macros.h" +#include "util/directiontables.h" +#include "util/pointedthing.h" +#include "util/quicktune_shortcutter.h" +#include "irrlicht_changes/static_text.h" +#include "version.h" +#include "script/scripting_client.h" +#include "hud.h" #include "irrlichttypes.h" #include <string> @@ -42,6 +94,853 @@ struct CameraOrientation { f32 camera_pitch; // "up/down" }; +/* + Text input system +*/ + +struct TextDestNodeMetadata : public TextDest +{ + TextDestNodeMetadata(v3s16 p, Client *client) + { + m_p = p; + m_client = client; + } + // This is deprecated I guess? -celeron55 + void gotText(const std::wstring &text) + { + std::string ntext = wide_to_utf8(text); + infostream << "Submitting 'text' field of node at (" << m_p.X << "," + << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; + StringMap fields; + fields["text"] = ntext; + m_client->sendNodemetaFields(m_p, "", fields); + } + void gotText(const StringMap &fields) + { + m_client->sendNodemetaFields(m_p, "", fields); + } + + v3s16 m_p; + Client *m_client; +}; + +struct TextDestPlayerInventory : public TextDest +{ + TextDestPlayerInventory(Client *client) + { + m_client = client; + m_formname = ""; + } + TextDestPlayerInventory(Client *client, const std::string &formname) + { + m_client = client; + m_formname = formname; + } + void gotText(const StringMap &fields) + { + m_client->sendInventoryFields(m_formname, fields); + } + + Client *m_client; +}; + +struct LocalFormspecHandler : public TextDest +{ + LocalFormspecHandler(const std::string &formname) + { + m_formname = formname; + } + + LocalFormspecHandler(const std::string &formname, Client *client): + m_client(client) + { + m_formname = formname; + } + + void gotText(const StringMap &fields) + { + if (m_formname == "MT_PAUSE_MENU") { + if (fields.find("btn_sound") != fields.end()) { + g_gamecallback->changeVolume(); + return; + } + + if (fields.find("btn_key_config") != fields.end()) { + g_gamecallback->keyConfig(); + return; + } + + if (fields.find("btn_exit_menu") != fields.end()) { + g_gamecallback->disconnect(); + return; + } + + if (fields.find("btn_exit_os") != fields.end()) { + g_gamecallback->exitToOS(); +#ifndef __ANDROID__ + RenderingEngine::get_raw_device()->closeDevice(); +#endif + return; + } + + if (fields.find("btn_change_password") != fields.end()) { + g_gamecallback->changePassword(); + return; + } + + if (fields.find("quit") != fields.end()) { + return; + } + + if (fields.find("btn_continue") != fields.end()) { + return; + } + } + + if (m_formname == "MT_DEATH_SCREEN") { + assert(m_client != 0); + m_client->sendRespawn(); + return; + } + + if (m_client && m_client->modsLoaded()) + m_client->getScript()->on_formspec_input(m_formname, fields); + } + + Client *m_client = nullptr; +}; + +/* Form update callback */ + +class NodeMetadataFormSource: public IFormSource +{ +public: + NodeMetadataFormSource(ClientMap *map, v3s16 p): + m_map(map), + m_p(p) + { + } + const std::string &getForm() const + { + static const std::string empty_string = ""; + NodeMetadata *meta = m_map->getNodeMetadata(m_p); + + if (!meta) + return empty_string; + + return meta->getString("formspec"); + } + + virtual std::string resolveText(const std::string &str) + { + NodeMetadata *meta = m_map->getNodeMetadata(m_p); + + if (!meta) + return str; + + return meta->resolveString(str); + } + + ClientMap *m_map; + v3s16 m_p; +}; + +class PlayerInventoryFormSource: public IFormSource +{ +public: + PlayerInventoryFormSource(Client *client): + m_client(client) + { + } + + const std::string &getForm() const + { + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + return player->inventory_formspec; + } + + Client *m_client; +}; + +class NodeDugEvent: public MtEvent +{ +public: + v3s16 p; + MapNode n; + + NodeDugEvent(v3s16 p, MapNode n): + p(p), + n(n) + {} + MtEvent::Type getType() const + { + return MtEvent::NODE_DUG; + } +}; + +class SoundMaker +{ + ISoundManager *m_sound; + const NodeDefManager *m_ndef; +public: + bool makes_footstep_sound; + float m_player_step_timer; + float m_player_jump_timer; + + SimpleSoundSpec m_player_step_sound; + SimpleSoundSpec m_player_leftpunch_sound; + SimpleSoundSpec m_player_rightpunch_sound; + + SoundMaker(ISoundManager *sound, const NodeDefManager *ndef): + m_sound(sound), + m_ndef(ndef), + makes_footstep_sound(true), + m_player_step_timer(0.0f), + m_player_jump_timer(0.0f) + { + } + + void playPlayerStep() + { + if (m_player_step_timer <= 0 && m_player_step_sound.exists()) { + m_player_step_timer = 0.03; + if (makes_footstep_sound) + m_sound->playSound(m_player_step_sound, false); + } + } + + void playPlayerJump() + { + if (m_player_jump_timer <= 0.0f) { + m_player_jump_timer = 0.2f; + m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false); + } + } + + static void viewBobbingStep(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->playPlayerStep(); + } + + static void playerRegainGround(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->playPlayerStep(); + } + + static void playerJump(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->playPlayerJump(); + } + + static void cameraPunchLeft(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->m_sound->playSound(sm->m_player_leftpunch_sound, false); + } + + static void cameraPunchRight(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->m_sound->playSound(sm->m_player_rightpunch_sound, false); + } + + static void nodeDug(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + NodeDugEvent *nde = (NodeDugEvent *)e; + sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false); + } + + static void playerDamage(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false); + } + + static void playerFallingDamage(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker *)data; + sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false); + } + + void registerReceiver(MtEventManager *mgr) + { + mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this); + mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this); + mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this); + mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this); + mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this); + mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this); + mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this); + mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this); + } + + void step(float dtime) + { + m_player_step_timer -= dtime; + m_player_jump_timer -= dtime; + } +}; + +// Locally stored sounds don't need to be preloaded because of this +class GameOnDemandSoundFetcher: public OnDemandSoundFetcher +{ + std::set<std::string> m_fetched; +private: + void paths_insert(std::set<std::string> &dst_paths, + const std::string &base, + const std::string &name) + { + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg"); + dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg"); + } +public: + void fetchSounds(const std::string &name, + std::set<std::string> &dst_paths, + std::set<std::string> &dst_datas) + { + if (m_fetched.count(name)) + return; + + m_fetched.insert(name); + + paths_insert(dst_paths, porting::path_share, name); + paths_insert(dst_paths, porting::path_user, name); + } +}; + + +// before 1.8 there isn't a "integer interface", only float +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) +typedef f32 SamplerLayer_t; +#else +typedef s32 SamplerLayer_t; +#endif + + +class GameGlobalShaderConstantSetter : public IShaderConstantSetter +{ + Sky *m_sky; + bool *m_force_fog_off; + f32 *m_fog_range; + bool m_fog_enabled; + CachedPixelShaderSetting<float, 4> m_sky_bg_color; + CachedPixelShaderSetting<float> m_fog_distance; + CachedVertexShaderSetting<float> m_animation_timer_vertex; + CachedPixelShaderSetting<float> m_animation_timer_pixel; + CachedPixelShaderSetting<float, 3> m_day_light; + CachedPixelShaderSetting<float, 4> m_star_color; + CachedPixelShaderSetting<float, 3> m_eye_position_pixel; + CachedVertexShaderSetting<float, 3> m_eye_position_vertex; + CachedPixelShaderSetting<float, 3> m_minimap_yaw; + CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; + CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; + CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; + Client *m_client; + +public: + void onSettingsChange(const std::string &name) + { + if (name == "enable_fog") + m_fog_enabled = g_settings->getBool("enable_fog"); + } + + static void settingsCallback(const std::string &name, void *userdata) + { + reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name); + } + + void setSky(Sky *sky) { m_sky = sky; } + + GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off, + f32 *fog_range, Client *client) : + m_sky(sky), + m_force_fog_off(force_fog_off), + m_fog_range(fog_range), + m_sky_bg_color("skyBgColor"), + m_fog_distance("fogDistance"), + m_animation_timer_vertex("animationTimer"), + m_animation_timer_pixel("animationTimer"), + m_day_light("dayLight"), + m_star_color("starColor"), + m_eye_position_pixel("eyePosition"), + m_eye_position_vertex("eyePosition"), + m_minimap_yaw("yawVec"), + m_camera_offset_pixel("cameraOffset"), + m_camera_offset_vertex("cameraOffset"), + m_base_texture("baseTexture"), + m_client(client) + { + g_settings->registerChangedCallback("enable_fog", settingsCallback, this); + m_fog_enabled = g_settings->getBool("enable_fog"); + } + + ~GameGlobalShaderConstantSetter() + { + g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this); + } + + virtual void onSetConstants(video::IMaterialRendererServices *services, + bool is_highlevel) + { + if (!is_highlevel) + return; + + // Background color + video::SColor bgcolor = m_sky->getBgColor(); + video::SColorf bgcolorf(bgcolor); + float bgcolorfa[4] = { + bgcolorf.r, + bgcolorf.g, + bgcolorf.b, + bgcolorf.a, + }; + m_sky_bg_color.set(bgcolorfa, services); + + // Fog distance + float fog_distance = 10000 * BS; + + if (m_fog_enabled && !*m_force_fog_off) + fog_distance = *m_fog_range; + + m_fog_distance.set(&fog_distance, services); + + u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio(); + video::SColorf sunlight; + get_sunlight_color(&sunlight, daynight_ratio); + float dnc[3] = { + sunlight.r, + sunlight.g, + sunlight.b }; + m_day_light.set(dnc, services); + + video::SColorf star_color = m_sky->getCurrentStarColor(); + float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a}; + m_star_color.set(clr, services); + + u32 animation_timer = porting::getTimeMs() % 1000000; + float animation_timer_f = (float)animation_timer / 100000.f; + m_animation_timer_vertex.set(&animation_timer_f, services); + m_animation_timer_pixel.set(&animation_timer_f, services); + + float eye_position_array[3]; + v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + eye_position_array[0] = epos.X; + eye_position_array[1] = epos.Y; + eye_position_array[2] = epos.Z; +#else + epos.getAs3Values(eye_position_array); +#endif + m_eye_position_pixel.set(eye_position_array, services); + m_eye_position_vertex.set(eye_position_array, services); + + if (m_client->getMinimap()) { + float minimap_yaw_array[3]; + v3f minimap_yaw = m_client->getMinimap()->getYawVec(); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + minimap_yaw_array[0] = minimap_yaw.X; + minimap_yaw_array[1] = minimap_yaw.Y; + minimap_yaw_array[2] = minimap_yaw.Z; +#else + minimap_yaw.getAs3Values(minimap_yaw_array); +#endif + m_minimap_yaw.set(minimap_yaw_array, services); + } + + float camera_offset_array[3]; + v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + camera_offset_array[0] = offset.X; + camera_offset_array[1] = offset.Y; + camera_offset_array[2] = offset.Z; +#else + offset.getAs3Values(camera_offset_array); +#endif + m_camera_offset_pixel.set(camera_offset_array, services); + m_camera_offset_vertex.set(camera_offset_array, services); + + SamplerLayer_t base_tex = 0; + m_base_texture.set(&base_tex, services); + } +}; + + +class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ + Sky *m_sky; + bool *m_force_fog_off; + f32 *m_fog_range; + Client *m_client; + std::vector<GameGlobalShaderConstantSetter *> created_nosky; +public: + GameGlobalShaderConstantSetterFactory(bool *force_fog_off, + f32 *fog_range, Client *client) : + m_sky(NULL), + m_force_fog_off(force_fog_off), + m_fog_range(fog_range), + m_client(client) + {} + + void setSky(Sky *sky) { + m_sky = sky; + for (GameGlobalShaderConstantSetter *ggscs : created_nosky) { + ggscs->setSky(m_sky); + } + created_nosky.clear(); + } + + virtual IShaderConstantSetter* create() + { + GameGlobalShaderConstantSetter *scs = new GameGlobalShaderConstantSetter( + m_sky, m_force_fog_off, m_fog_range, m_client); + if (!m_sky) + created_nosky.push_back(scs); + return scs; + } +}; + +#ifdef __ANDROID__ +#define SIZE_TAG "size[11,5.5]" +#else +#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop +#endif + +/**************************************************************************** + ****************************************************************************/ + +const float object_hit_delay = 0.2; + +struct FpsControl { + u32 last_time, busy_time, sleep_time; +}; + + +/* The reason the following structs are not anonymous structs within the + * class is that they are not used by the majority of member functions and + * many functions that do require objects of thse types do not modify them + * (so they can be passed as a const qualified parameter) + */ + +struct GameRunData { + u16 dig_index; + u16 new_playeritem; + PointedThing pointed_old; + bool digging; + bool punching; + bool btn_down_for_dig; + bool dig_instantly; + bool digging_blocked; + bool left_punch; + bool reset_jump_timer; + float nodig_delay_timer; + float dig_time; + float dig_time_complete; + float repeat_place_timer; + float object_hit_delay_timer; + float time_from_last_punch; + ClientActiveObject *selected_object; + + float jump_timer; + float damage_flash; + float update_draw_list_timer; + + f32 fog_range; + + v3f update_draw_list_last_cam_dir; + + float time_of_day_smooth; +}; + +class Game; + +struct ClientEventHandler +{ + void (Game::*handler)(ClientEvent *, CameraOrientation *); +}; + +class Game { +public: + Game(); + ~Game(); + + bool startup(bool *kill, + InputHandler *input, + const GameStartData &game_params, + std::string &error_message, + bool *reconnect, + ChatBackend *chat_backend); + + + void run(); + void shutdown(); + + void extendedResourceCleanup(); + + // Basic initialisation + bool init(const std::string &map_dir, const std::string &address, + u16 port, const SubgameSpec &gamespec); + bool initSound(); + bool createSingleplayerServer(const std::string &map_dir, + const SubgameSpec &gamespec, u16 port); + + // Client creation + bool createClient(const GameStartData &start_data); + bool initGui(); + + // Client connection + bool connectToServer(const GameStartData &start_data, + bool *connect_ok, bool *aborted); + bool getServerContent(bool *aborted); + + // Main loop + + void updateInteractTimers(f32 dtime); + bool checkConnection(); + bool handleCallbacks(); + void processQueues(); + void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime); + void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); + void updateProfilerGraphs(ProfilerGraph *graph); + + // Input related + void processUserInput(f32 dtime); + void processKeyInput(); + void processItemSelection(u16 *new_playeritem); + + void dropSelectedItem(bool single_item = false); + void openInventory(); + void openEnderchest(); + void openConsole(float scale, const wchar_t *line=NULL); + void toggleFreeMove(); + void toggleFreeMoveAlt(); + void togglePitchMove(); + void toggleFast(); + void toggleNoClip(); + void toggleKillaura(); + void toggleFreecam(); + void toggleScaffold(); + void toggleNextItem(); + void toggleCinematic(); + void toggleAutoforward(); + + void toggleMinimap(bool shift_pressed); + void toggleFog(); + void toggleDebug(); + void toggleUpdateCamera(); + void updatePlayerCAOVisibility(); + + void increaseViewRange(); + void decreaseViewRange(); + void toggleFullViewRange(); + void checkZoomEnabled(); + + void updateCameraDirection(CameraOrientation *cam, float dtime); + void updateCameraOrientation(CameraOrientation *cam, float dtime); + void updatePlayerControl(const CameraOrientation &cam); + void step(f32 *dtime); + void processClientEvents(CameraOrientation *cam); + void updateCamera(u32 busy_time, f32 dtime); + void updateSound(f32 dtime); + void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug); + /*! + * Returns the object or node the player is pointing at. + * Also updates the selected thing in the Hud. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + * @param[in] camera_offset offset of the camera + * @param[out] selected_object the selected object or + * NULL if not found + */ + PointedThing updatePointedThing( + const core::line3d<f32> &shootline, bool liquids_pointable, + bool look_for_object, const v3s16 &camera_offset); + void handlePointingAtNothing(const ItemStack &playerItem); + void handlePointingAtNode(const PointedThing &pointed, + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); + void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, + const v3f &player_position, bool show_debug); + void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); + void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, + const CameraOrientation &cam); + + // Misc + void limitFps(FpsControl *fps_timings, f32 *dtime); + + void showOverlayMessage(const char *msg, float dtime, int percent, + bool draw_clouds = true); + + static void freecamChangedCallback(const std::string &setting_name, void *data); + static void settingChangedCallback(const std::string &setting_name, void *data); + static void updateAllMapBlocksCallback(const std::string &setting_name, void *data); + void readSettings(); + + inline bool isKeyDown(GameKeyType k) + { + return input->isKeyDown(k); + } + inline bool wasKeyDown(GameKeyType k) + { + return input->wasKeyDown(k); + } + inline bool wasKeyPressed(GameKeyType k) + { + return input->wasKeyPressed(k); + } + inline bool wasKeyReleased(GameKeyType k) + { + return input->wasKeyReleased(k); + } + +#ifdef __ANDROID__ + void handleAndroidChatInput(); +#endif + + struct Flags { + bool force_fog_off = false; + bool disable_camera_update = false; + }; + + void showDeathFormspec(); + void showPauseMenu(); + + // ClientEvent handlers + void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_HandleParticleEvent(ClientEvent *event, + CameraOrientation *cam); + void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, + CameraOrientation *cam); + void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam); + + void updateChat(f32 dtime, const v2u32 &screensize); + + bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, + const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, + const NodeMetadata *meta); + static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; + + InputHandler *input = nullptr; + + Client *client = nullptr; + Server *server = nullptr; + + IWritableTextureSource *texture_src = nullptr; + IWritableShaderSource *shader_src = nullptr; + + // When created, these will be filled with data received from the server + IWritableItemDefManager *itemdef_manager = nullptr; + NodeDefManager *nodedef_manager = nullptr; + + GameOnDemandSoundFetcher soundfetcher; // useful when testing + ISoundManager *sound = nullptr; + bool sound_is_dummy = false; + SoundMaker *soundmaker = nullptr; + + ChatBackend *chat_backend = nullptr; + LogOutputBuffer m_chat_log_buf; + + EventManager *eventmgr = nullptr; + QuicktuneShortcutter *quicktune = nullptr; + bool registration_confirmation_shown = false; + + std::unique_ptr<GameUI> m_game_ui; + GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop() + CheatMenu *m_cheat_menu = nullptr; + MapDrawControl *draw_control = nullptr; + Camera *camera = nullptr; + Clouds *clouds = nullptr; // Free using ->Drop() + Sky *sky = nullptr; // Free using ->Drop() + Hud *hud = nullptr; + Minimap *mapper = nullptr; + + GameRunData runData; + Flags m_flags; + + /* 'cache' + This class does take ownership/responsibily for cleaning up etc of any of + these items (e.g. device) + */ + IrrlichtDevice *device; + video::IVideoDriver *driver; + scene::ISceneManager *smgr; + bool *kill; + std::string *error_message; + bool *reconnect_requested; + scene::ISceneNode *skybox; + + bool simple_singleplayer_mode; + /* End 'cache' */ + + /* Pre-calculated values + */ + int crack_animation_length; + + IntervalLimiter profiler_interval; + + /* + * TODO: Local caching of settings is not optimal and should at some stage + * be updated to use a global settings object for getting thse values + * (as opposed to the this local caching). This can be addressed in + * a later release. + */ + bool m_cache_doubletap_jump; + bool m_cache_enable_clouds; + bool m_cache_enable_joysticks; + bool m_cache_enable_particles; + bool m_cache_enable_fog; + bool m_cache_enable_noclip; + bool m_cache_enable_free_move; + f32 m_cache_mouse_sensitivity; + f32 m_cache_joystick_frustum_sensitivity; + f32 m_repeat_place_time; + f32 m_cache_cam_smoothing; + f32 m_cache_fog_start; + + bool m_invert_mouse = false; + bool m_first_loop_after_window_activation = false; + bool m_camera_offset_changed = false; + + bool m_does_lost_focus_pause_game = false; + + CameraOrientation cam_view_target = { 0 }; + CameraOrientation cam_view = { 0 }; + + int m_reset_HW_buffer_counter = 0; +#ifdef __ANDROID__ + bool m_cache_hold_aux1; + bool m_android_chat_open; +#endif +}; +extern Game *g_game; void the_game(bool *kill, InputHandler *input, diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 0c08efeb5..0c1da3915 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -53,6 +53,9 @@ GameUI::GameUI() } void GameUI::init() { + m_guitext_coords = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false, + false, guiroot); + // First line of debug text m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(), core::rect<s32>(0, 0, 0, 0), false, false, guiroot); @@ -97,8 +100,24 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ const CameraOrientation &cam, const PointedThing &pointed_old, const GUIChatConsole *chat_console, float dtime) { + LocalPlayer *player = client->getEnv().getLocalPlayer(); + v3f player_position = player->getPosition(); v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); + bool show_coords = g_settings->getBool("coords"); + + if (show_coords) { + std::ostringstream os(std::ios_base::binary); + os << std::setprecision(1) << std::fixed + << (player_position.X / BS) + << ", " << (player_position.Y / BS) + << ", " << (player_position.Z / BS); + setStaticText(m_guitext_coords, utf8_to_wide(os.str()).c_str()); + m_guitext_coords->setRelativePosition(core::rect<s32>(5, screensize.Y - 5 - g_fontengine->getTextHeight(), screensize.X, screensize.Y)); + } + + m_guitext_coords->setVisible(show_coords); + if (m_flags.show_debug) { static float drawtime_avg = 0; drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05; @@ -128,9 +147,6 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ m_guitext->setVisible(m_flags.show_debug); if (m_flags.show_debug) { - LocalPlayer *player = client->getEnv().getLocalPlayer(); - v3f player_position = player->getPosition(); - std::ostringstream os(std::ios_base::binary); os << std::setprecision(1) << std::fixed << "pos: (" << (player_position.X / BS) @@ -197,8 +213,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ m_guitext_status->enableOverrideColor(true); } - // Hide chat when console is visible - m_guitext_chat->setVisible(isChatVisible() && !chat_console->isVisible()); + m_guitext_chat->setVisible(isChatVisible()); } void GameUI::initFlags() @@ -223,13 +238,14 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) { // Update gui element size and position - s32 chat_y = 5; + + const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); + + s32 chat_y = window_size.Y - 150 - m_guitext_chat->getTextHeight(); if (m_flags.show_debug) chat_y += 2 * g_fontengine->getLineHeight(); - const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); - core::rect<s32> chat_size(10, chat_y, window_size.X - 20, 0); chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y, @@ -277,6 +293,15 @@ void GameUI::toggleChat() showTranslatedStatusText("Chat hidden"); } +void GameUI::toggleCheatMenu() +{ + m_flags.show_cheat_menu = !m_flags.show_cheat_menu; + if (m_flags.show_cheat_menu) + showTranslatedStatusText("Cheat Menu shown"); + else + showTranslatedStatusText("Cheat Menu hidden"); +} + void GameUI::toggleHud() { m_flags.show_hud = !m_flags.show_hud; diff --git a/src/client/gameui.h b/src/client/gameui.h index 67c6a9921..8a1b5650d 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -59,6 +59,7 @@ public: bool show_minimap = false; bool show_debug = true; bool show_profiler_graph = false; + bool show_cheat_menu = true; }; void init(); @@ -91,6 +92,7 @@ public: void updateProfiler(); void toggleChat(); + void toggleCheatMenu(); void toggleHud(); void toggleProfiler(); @@ -109,7 +111,8 @@ private: gui::IGUIStaticText *m_guitext = nullptr; // First line of debug text gui::IGUIStaticText *m_guitext2 = nullptr; // Second line of debug text - + gui::IGUIStaticText *m_guitext_coords = nullptr; + gui::IGUIStaticText *m_guitext_info = nullptr; // At the middle of the screen std::wstring m_infotext; diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 608a405a8..37ab838c0 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -44,6 +44,7 @@ void KeyCache::populate() key[KeyType::DROP] = getKeySetting("keymap_drop"); key[KeyType::INVENTORY] = getKeySetting("keymap_inventory"); + key[KeyType::ENDERCHEST] = getKeySetting("keymap_enderchest"); key[KeyType::CHAT] = getKeySetting("keymap_chat"); key[KeyType::CMD] = getKeySetting("keymap_cmd"); key[KeyType::CMD_LOCAL] = getKeySetting("keymap_cmd_local"); @@ -63,6 +64,7 @@ void KeyCache::populate() key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud"); key[KeyType::TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat"); key[KeyType::TOGGLE_FOG] = getKeySetting("keymap_toggle_fog"); + key[KeyType::TOGGLE_CHEAT_MENU] = getKeySetting("keymap_toggle_cheat_menu"); key[KeyType::TOGGLE_UPDATE_CAMERA] = getKeySetting("keymap_toggle_update_camera"); key[KeyType::TOGGLE_DEBUG] = getKeySetting("keymap_toggle_debug"); key[KeyType::TOGGLE_PROFILER] = getKeySetting("keymap_toggle_profiler"); @@ -73,6 +75,15 @@ void KeyCache::populate() getKeySetting("keymap_decrease_viewing_range_min"); key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect"); key[KeyType::ZOOM] = getKeySetting("keymap_zoom"); + key[KeyType::KILLAURA] = getKeySetting("keymap_toggle_killaura"); + key[KeyType::FREECAM] = getKeySetting("keymap_toggle_freecam"); + key[KeyType::SCAFFOLD] = getKeySetting("keymap_toggle_scaffold"); + key[KeyType::NEXT_ITEM] = getKeySetting("keymap_toggle_next_item"); + key[KeyType::SELECT_UP] = getKeySetting("keymap_select_up"); + key[KeyType::SELECT_DOWN] = getKeySetting("keymap_select_down"); + key[KeyType::SELECT_LEFT] = getKeySetting("keymap_select_left"); + key[KeyType::SELECT_RIGHT] = getKeySetting("keymap_select_right"); + key[KeyType::SELECT_CONFIRM] = getKeySetting("keymap_select_confirm"); key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index def147a82..b3a7d4ba3 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -197,7 +197,6 @@ public: TouchScreenGUI *m_touchscreengui; #endif -private: // The current state of keys KeyList keyIsDown; @@ -235,6 +234,8 @@ public: } virtual bool isKeyDown(GameKeyType k) = 0; + virtual void setKeypress(const KeyPress &keyCode) = 0; + virtual void unsetKeypress(const KeyPress &keyCode) = 0; virtual bool wasKeyDown(GameKeyType k) = 0; virtual bool wasKeyPressed(GameKeyType k) = 0; virtual bool wasKeyReleased(GameKeyType k) = 0; @@ -273,6 +274,15 @@ public: { return m_receiver->IsKeyDown(keycache.key[k]) || joystick.isKeyDown(k); } + virtual void setKeypress(const KeyPress &keyCode) + { + m_receiver->keyIsDown.set(keyCode); + m_receiver->keyWasDown.set(keyCode); + } + virtual void unsetKeypress(const KeyPress &keyCode) + { + m_receiver->keyIsDown.unset(keyCode); + } virtual bool wasKeyDown(GameKeyType k) { return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k); @@ -332,7 +342,7 @@ public: m_receiver->clearInput(); } -private: + private: MyEventReceiver *m_receiver = nullptr; v2s32 m_mousepos; }; @@ -348,6 +358,14 @@ public: } virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; } + virtual void setKeypress(const KeyPress &keyCode) + { + keydown.set(keyCode); + } + virtual void unsetKeypress(const KeyPress &keyCode) + { + keydown.unset(keyCode); + } virtual bool wasKeyDown(GameKeyType k) { return false; } virtual bool wasKeyPressed(GameKeyType k) { return false; } virtual bool wasKeyReleased(GameKeyType k) { return false; } diff --git a/src/client/keys.h b/src/client/keys.h index 60a7a3c45..828ea3ec4 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -43,6 +43,7 @@ public: // Other DROP, INVENTORY, + ENDERCHEST, CHAT, CMD, CMD_LOCAL, @@ -62,6 +63,7 @@ public: TOGGLE_HUD, TOGGLE_CHAT, TOGGLE_FOG, + TOGGLE_CHEAT_MENU, TOGGLE_UPDATE_CAMERA, TOGGLE_DEBUG, TOGGLE_PROFILER, @@ -70,7 +72,16 @@ public: DECREASE_VIEWING_RANGE, RANGESELECT, ZOOM, - + KILLAURA, + FREECAM, + SCAFFOLD, + NEXT_ITEM, + SELECT_UP, + SELECT_DOWN, + SELECT_LEFT, + SELECT_RIGHT, + SELECT_CONFIRM, + QUICKTUNE_NEXT, QUICKTUNE_PREV, QUICKTUNE_INC, diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index f3eb1a2dd..7d532c602 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "client.h" #include "content_cao.h" +#include "util/pointedthing.h" +#include "client/game.h" /* LocalPlayer @@ -87,7 +89,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, new_sneak_node_exists = false; } else { node = map->getNode(current_node, &is_valid_position); - if (!is_valid_position || !nodemgr->get(node).walkable) + if (!is_valid_position || nodemgr->get(node).walkable) new_sneak_node_exists = false; } @@ -113,7 +115,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, // The node to be sneaked on has to be walkable node = map->getNode(p, &is_valid_position); - if (!is_valid_position || !nodemgr->get(node).walkable) + if (!is_valid_position || ! nodemgr->get(node).walkable) continue; // And the node(s) above have to be nonwalkable bool ok = true; @@ -130,7 +132,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, } else { // legacy behaviour: check just one node node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position); - ok = is_valid_position && !nodemgr->get(node).walkable; + ok = is_valid_position && ! nodemgr->get(node).walkable; } if (!ok) continue; @@ -159,7 +161,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, node = map->getNode(m_sneak_node + v3s16(0, 3, 0), &is_valid_position); m_sneak_ladder_detected = is_valid_position && - !nodemgr->get(node).walkable; + ! nodemgr->get(node).walkable; } } return true; @@ -168,6 +170,9 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector<CollisionInfo> *collision_info) { + if (m_cao && m_cao->m_waiting_for_reattach > 0) + m_cao->m_waiting_for_reattach -= dtime; + // Node at feet position, update each ClientEnvironment::step() if (!collision_info || collision_info->empty()) m_standing_node = floatToInt(m_position, BS); @@ -296,7 +301,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, - &position, &m_speed, accel_f); + &position, &m_speed, accel_f, NULL, true, true); bool could_sneak = control.sneak && !free_move && !in_liquid && !is_climbing && physics_override_sneak; @@ -456,6 +461,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, m_speed.Y += jumpspeed; } setSpeed(m_speed); + if (! m_freecam) + m_legit_speed = m_speed; m_can_jump = false; } @@ -604,14 +611,14 @@ void LocalPlayer::applyControl(float dtime, Environment *env) else speedV.Y = movement_speed_walk; } - } else if (m_can_jump) { + } else if (m_can_jump || g_settings->getBool("jetpack")) { /* NOTE: The d value in move() affects jump height by raising the height at which the jump speed is kept at its starting value */ v3f speedJ = getSpeed(); - if (speedJ.Y >= -0.5f * BS) { + if (speedJ.Y >= -0.5f * BS || g_settings->getBool("jetpack")) { speedJ.Y = movement_speed_jump * physics_override_jump; setSpeed(speedJ); m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP)); @@ -634,7 +641,7 @@ void LocalPlayer::applyControl(float dtime, Environment *env) if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb)) speedH = speedH.normalize() * movement_speed_fast; - else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable) + else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable && !g_settings->getBool("no_slow")) speedH = speedH.normalize() * movement_speed_crouch; else speedH = speedH.normalize() * movement_speed_walk; @@ -712,7 +719,7 @@ v3f LocalPlayer::getEyeOffset() const ClientActiveObject *LocalPlayer::getParent() const { - return m_cao ? m_cao->getParent() : nullptr; + return (m_cao && ! g_settings->getBool("entity_speed")) ? m_cao->getParent() : nullptr; } bool LocalPlayer::isDead() const @@ -721,6 +728,18 @@ bool LocalPlayer::isDead() const return !getCAO()->isImmortal() && hp == 0; } +void LocalPlayer::tryReattach(int id) +{ + PointedThing pointed(id, v3f(0, 0, 0), v3s16(0, 0, 0), 0); + m_client->interact(INTERACT_PLACE, pointed); + m_cao->m_waiting_for_reattach = 10; +} + +bool LocalPlayer::isWaitingForReattach() const +{ + return g_settings->getBool("entity_speed") && m_cao && ! m_cao->getParent() && m_cao->m_waiting_for_reattach > 0; +} + // 3D acceleration void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H, const f32 max_increase_V, const bool use_pitch) @@ -904,7 +923,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, - &position, &m_speed, accel_f); + &position, &m_speed, accel_f, NULL, true, true); // Positition was slightly changed; update standing node pos if (touching_ground) @@ -1083,7 +1102,7 @@ float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH) Map *map = &env->getMap(); const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos())); int slippery = 0; - if (f.walkable) + if (f.walkable && ! g_settings->getBool("antislip")) slippery = itemgroup_get(f.groups, "slippery"); if (slippery >= 1) { @@ -1156,7 +1175,7 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, // try at peak of jump, zero step height collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d, - m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f)); + m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), NULL, true, true); // see if we can get a little bit farther horizontally if we had // jumped @@ -1169,3 +1188,4 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, m_autojump_time = 0.1f; } } + diff --git a/src/client/localplayer.h b/src/client/localplayer.h index 345aec9d9..0e071d2b4 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -31,6 +31,7 @@ class GenericCAO; class ClientActiveObject; class ClientEnvironment; class IGameDef; +struct ContentFeatures; struct collisionMoveResult; enum LocalPlayerAnimations @@ -131,11 +132,37 @@ public: inline void setPosition(const v3f &position) { m_position = position; + if (! m_freecam) + m_legit_position = position; m_sneak_node_exists = false; } v3f getPosition() const { return m_position; } + v3f getLegitPosition() const { return m_legit_position; } + + v3f getLegitSpeed() const { return m_legit_speed; } + + inline void setLegitPosition(const v3f &position) + { + if (m_freecam) + m_legit_position = position; + else + setPosition(position); + } + + inline void freecamEnable() + { + m_freecam = true; + } + + inline void freecamDisable() + { + m_freecam = false; + setPosition(m_legit_position); + setSpeed(m_legit_speed); + } + // Non-transformed eye offset getters // For accurate positions, use the Camera functions v3f getEyePosition() const { return m_position + getEyeOffset(); } @@ -157,7 +184,13 @@ public: { added_velocity += vel; } - + + void tryReattach(int id); + + bool isWaitingForReattach() const; + + bool canWalkOn(const ContentFeatures &f); + private: void accelerate(const v3f &target_speed, const f32 max_increase_H, const f32 max_increase_V, const bool use_pitch); @@ -168,7 +201,10 @@ private: const v3f &position_before_move, const v3f &speed_before_move, f32 pos_max_d); + bool m_freecam = false; v3f m_position; + v3f m_legit_position; + v3f m_legit_speed; v3s16 m_standing_node; v3s16 m_sneak_node = v3s16(32767, 32767, 32767); diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 6a59fabe3..7b342fc33 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -87,7 +87,7 @@ void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos) void MeshMakeData::setSmoothLighting(bool smooth_lighting) { - m_smooth_lighting = smooth_lighting; + m_smooth_lighting = smooth_lighting && ! g_settings->getBool("fullbright"); } /* @@ -104,6 +104,8 @@ static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment, u8 light = n.getLight(bank, ndef); if (light > 0) light = rangelim(light + increment, 0, LIGHT_SUN); + if(g_settings->getBool("fullbright")) + return 255; return decode_light(light); } @@ -138,7 +140,8 @@ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, ndef->get(n2).light_source); if(light_source > light) light = light_source; - + if(g_settings->getBool("fullbright")) + return 255; return decode_light(light); } @@ -656,6 +659,7 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, u8 c1 = f1.solidness; u8 c2 = f2.solidness; + if (c1 == c2) return 0; @@ -664,6 +668,7 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, else if (c2 == 0) c2 = f2.visual_solidness; + if (c1 == c2) { *equivalent = true; // If same solidness, liquid takes precense @@ -763,6 +768,24 @@ void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *dat tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1]; } +std::set<content_t> splitToContentT(std::string str, const NodeDefManager *ndef) +{ + str += "\n"; + std::set<content_t> dat; + std::string buf; + for (char c : str) { + if (c == ',' || c == '\n') { + if (! buf.empty()) { + dat.insert(ndef->getId(buf)); + } + buf.clear(); + } else if (c != ' ') { + buf += c; + } + } + return dat; +} + static void getTileInfo( // Input: MeshMakeData *data, @@ -774,31 +797,40 @@ static void getTileInfo( v3s16 &face_dir_corrected, u16 *lights, u8 &waving, - TileSpec &tile - ) + TileSpec &tile, + // lol more Input + bool xray, + std::set<content_t> xraySet) { VoxelManipulator &vmanip = data->m_vmanip; const NodeDefManager *ndef = data->m_client->ndef(); v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; - + const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p); + content_t c0 = n0.getContent(); + if (xray && xraySet.find(c0) != xraySet.end()) + c0 = CONTENT_AIR; // Don't even try to get n1 if n0 is already CONTENT_IGNORE - if (n0.getContent() == CONTENT_IGNORE) { + if (c0 == CONTENT_IGNORE) { makes_face = false; return; } const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir); - if (n1.getContent() == CONTENT_IGNORE) { + content_t c1 = n1.getContent(); + if (xray && xraySet.find(c1) != xraySet.end()) + c1 = CONTENT_AIR; + + if (c1 == CONTENT_IGNORE) { makes_face = false; return; } // This is hackish bool equivalent = false; - u8 mf = face_contents(n0.getContent(), n1.getContent(), + u8 mf = face_contents(c0, c1, &equivalent, ndef); if (mf == 0) { @@ -854,7 +886,9 @@ static void updateFastFaceRow( v3s16 translate_dir, const v3f &&translate_dir_f, const v3s16 &&face_dir, - std::vector<FastFace> &dest) + std::vector<FastFace> &dest, + bool xray, + std::set<content_t> xraySet) { static thread_local const bool waving_liquids = g_settings->getBool("enable_shaders") && @@ -874,7 +908,7 @@ static void updateFastFaceRow( // Get info of first tile getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected, - lights, waving, tile); + lights, waving, tile, xray, xraySet); // Unroll this variable which has a significant build cost TileSpec next_tile; @@ -891,13 +925,15 @@ static void updateFastFaceRow( // the face must be drawn anyway if (j != MAP_BLOCKSIZE - 1) { p += translate_dir; - + getTileInfo(data, p, face_dir, next_makes_face, next_p_corrected, next_face_dir_corrected, next_lights, waving, - next_tile); - + next_tile, + xray, + xraySet); + if (next_makes_face == makes_face && next_p_corrected == p_corrected + translate_dir && next_face_dir_corrected == face_dir_corrected @@ -946,7 +982,7 @@ static void updateFastFaceRow( } static void updateAllFastFaceRows(MeshMakeData *data, - std::vector<FastFace> &dest) + std::vector<FastFace> &dest, bool xray, std::set<content_t> xraySet) { /* Go through every y,z and get top(y+) faces in rows of x+ @@ -958,7 +994,9 @@ static void updateAllFastFaceRows(MeshMakeData *data, v3s16(1, 0, 0), //dir v3f (1, 0, 0), v3s16(0, 1, 0), //face dir - dest); + dest, + xray, + xraySet); /* Go through every x,y and get right(x+) faces in rows of z+ @@ -970,7 +1008,9 @@ static void updateAllFastFaceRows(MeshMakeData *data, v3s16(0, 0, 1), //dir v3f (0, 0, 1), v3s16(1, 0, 0), //face dir - dest); + dest, + xray, + xraySet); /* Go through every y,z and get back(z+) faces in rows of x+ @@ -982,7 +1022,9 @@ static void updateAllFastFaceRows(MeshMakeData *data, v3s16(1, 0, 0), //dir v3f (1, 0, 0), v3s16(0, 0, 1), //face dir - dest); + dest, + xray, + xraySet); } static void applyTileColor(PreMeshBuffer &pmb) @@ -1028,22 +1070,48 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): std::vector<FastFace> fastfaces_new; fastfaces_new.reserve(512); - + /* + X-Ray + */ + bool xray = g_settings->getBool("xray"); + std::set<content_t> xraySet, nodeESPSet; + if (xray) + xraySet = splitToContentT(g_settings->get("xray_nodes"), data->m_client->ndef()); + + nodeESPSet = splitToContentT(g_settings->get("node_esp_nodes"), data->m_client->ndef()); + /* We are including the faces of the trailing edges of the block. This means that when something changes, the caller must also update the meshes of the blocks at the leading edges. NOTE: This is the slowest part of this method. - */ + */ { // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) //TimeTaker timer2("updateAllFastFaceRows()"); - updateAllFastFaceRows(data, fastfaces_new); + updateAllFastFaceRows(data, fastfaces_new, xray, xraySet); } // End of slow part /* + NodeESP + */ + { + v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) { + for (s16 y = 0; y < MAP_BLOCKSIZE; y++) { + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) { + v3s16 pos = v3s16(x, y, z) + blockpos_nodes; + const MapNode &node = data->m_vmanip.getNodeRefUnsafeCheckFlags(pos); + if (nodeESPSet.find(node.getContent()) != nodeESPSet.end()) + esp_nodes.insert(pos); + } + } + } + } + + /* Convert FastFaces to MeshCollector */ diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 6a9c00ed5..ec1faea32 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -127,6 +127,8 @@ public: void updateCameraOffset(v3s16 camera_offset); + std::set<v3s16> esp_nodes; + private: scene::IMesh *m_mesh[MAX_TILE_LAYERS]; MinimapMapblock *m_minimap_mapblock; diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index f3c5e7da8..8e13dec3e 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -125,6 +125,6 @@ private: // TODO: Add callback to update these when g_settings changes int m_generation_interval; -protected: +public: virtual void doUpdate(); }; diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index d068ad5f7..60b095712 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -598,6 +598,9 @@ void Minimap::drawMinimap(core::rect<s32> rect) { core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); +// driver->setViewPort(core::rect<s32>( +// screensize.X - size * 2 - 10, 10, +// screensize.X - size - 10, size + 10)); driver->setViewPort(rect); driver->setTransform(video::ETS_PROJECTION, core::matrix4()); driver->setTransform(video::ETS_VIEW, core::matrix4()); @@ -652,6 +655,7 @@ void Minimap::drawMinimap(core::rect<s32> rect) { driver->setViewPort(oldViewPort); // Draw player markers +// v2s32 s_pos(screensize.X - size * 2 - 10, 10); v2s32 s_pos(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y); core::dimension2di imgsize(data->object_marker_red->getOriginalSize()); core::rect<s32> img_rect(0, 0, imgsize.Width, imgsize.Height); diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index 92a7137ea..44e3ed744 100644 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@ -18,12 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <iostream> #include "core.h" #include "client/camera.h" #include "client/client.h" #include "client/clientmap.h" #include "client/hud.h" #include "client/minimap.h" +#include "client/content_cao.h" +#include "mapblock.h" +#include "mapsector.h" RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud) : device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()), @@ -53,7 +57,7 @@ void RenderingCore::updateScreenSize() } void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap, - bool _draw_wield_tool, bool _draw_crosshair) + bool _draw_wield_tool, bool _draw_crosshair, bool _draw_esp, bool _draw_tracers, bool _draw_node_esp, bool _draw_node_tracers) { v2u32 ss = driver->getScreenSize(); if (screensize != ss) { @@ -65,11 +69,89 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min show_minimap = _show_minimap; draw_wield_tool = _draw_wield_tool; draw_crosshair = _draw_crosshair; - + draw_esp = _draw_esp; + draw_tracers = _draw_tracers; + draw_node_esp = _draw_node_esp; + draw_node_tracers = _draw_node_tracers; + beforeDraw(); drawAll(); } +void RenderingCore::drawTracersAndESP() +{ + bool only_trace_players = g_settings->getBool("only_trace_players"); + + ClientEnvironment &env = client->getEnv(); + Camera *camera = client->getCamera(); + + v3f camera_offset = intToFloat(camera->getOffset(), BS); + + v3f eye_pos = (camera->getPosition() + camera->getDirection() - camera_offset); + + video::SMaterial material, oldmaterial; + oldmaterial = driver->getMaterial2D(); + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_ZBUFFER, false); + material.setFlag(video::EMF_ZWRITE_ENABLE, false); + driver->setMaterial(material); + + if (draw_esp || draw_tracers) { + auto allObjects = env.getAllActiveObjects(); + + for (auto &it : allObjects) { + ClientActiveObject *cao = it.second; + if (cao->isLocalPlayer() || cao->getParent()) + continue; + GenericCAO *obj = dynamic_cast<GenericCAO *>(cao); + if (! obj) + continue; + if (only_trace_players && ! obj->isPlayer()) + continue; + aabb3f box; + if (! obj->getSelectionBox(&box)) + continue; + v3f pos = obj->getPosition() - camera_offset; + box.MinEdge += pos; + box.MaxEdge += pos; + if (draw_esp) + driver->draw3DBox(box, video::SColor(255, 255, 255, 255)); + if (draw_tracers) + driver->draw3DLine(eye_pos, box.getCenter(), video::SColor(255, 255, 255, 255)); + } + } + if (draw_node_esp || draw_node_tracers) { + Map &map = env.getMap(); + + std::vector<v3s16> positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 blockp : positions) { + MapBlock *block = map.getBlockNoCreate(blockp); + if (! block->mesh) + continue; + for (v3s16 p : block->mesh->esp_nodes) { + v3f pos = intToFloat(p, BS) - camera_offset; + MapNode node = map.getNode(p); + std::vector<aabb3f> boxes; + node.getSelectionBoxes(client->getNodeDefManager(), &boxes, node.getNeighbors(p, &map)); + video::SColor color = client->getNodeDefManager()->get(node).minimap_color; + for (aabb3f box : boxes) { + box.MinEdge += pos; + box.MaxEdge += pos; + if (draw_node_esp) + driver->draw3DBox(box, color); + if (draw_node_tracers) + driver->draw3DLine(eye_pos, box.getCenter(), color); + } + } + } + } + + driver->setMaterial(oldmaterial); +} + void RenderingCore::draw3D() { smgr->drawAll(); @@ -77,6 +159,8 @@ void RenderingCore::draw3D() if (!show_hud) return; hud->drawSelectionMesh(); + if (draw_esp || draw_tracers || draw_node_esp || draw_node_tracers) + drawTracersAndESP(); if (draw_wield_tool) camera->drawWieldedTool(); } diff --git a/src/client/render/core.h b/src/client/render/core.h index 52ea8f99f..2040155a6 100644 --- a/src/client/render/core.h +++ b/src/client/render/core.h @@ -36,6 +36,10 @@ protected: bool show_minimap; bool draw_wield_tool; bool draw_crosshair; + bool draw_esp; + bool draw_tracers; + bool draw_node_esp; + bool draw_node_tracers; IrrlichtDevice *device; video::IVideoDriver *driver; @@ -54,6 +58,7 @@ protected: virtual void beforeDraw() {} virtual void drawAll() = 0; + void drawTracersAndESP(); void draw3D(); void drawHUD(); void drawPostFx(); @@ -69,7 +74,8 @@ public: void initialize(); void draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap, - bool _draw_wield_tool, bool _draw_crosshair); + bool _draw_wield_tool, bool _draw_crosshair, bool _draw_esp, + bool _draw_tracers, bool _draw_node_esp, bool _draw_node_tracers); inline v2u32 getVirtualSize() const { return virtual_size; } }; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index f5aca8f58..e6d25d4ee 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -604,9 +604,9 @@ void RenderingEngine::_finalize() } void RenderingEngine::_draw_scene(video::SColor skycolor, bool show_hud, - bool show_minimap, bool draw_wield_tool, bool draw_crosshair) + bool show_minimap, bool draw_wield_tool, bool draw_crosshair, bool draw_esp, bool draw_tracers, bool draw_node_esp, bool draw_node_tracers) { - core->draw(skycolor, show_hud, show_minimap, draw_wield_tool, draw_crosshair); + core->draw(skycolor, show_hud, show_minimap, draw_wield_tool, draw_crosshair, draw_esp, draw_tracers, draw_node_esp, draw_node_tracers); } const char *RenderingEngine::getVideoDriverName(irr::video::E_DRIVER_TYPE type) diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 34cc60630..99aa3c678 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -117,10 +117,10 @@ public: } inline static void draw_scene(video::SColor skycolor, bool show_hud, - bool show_minimap, bool draw_wield_tool, bool draw_crosshair) + bool show_minimap, bool draw_wield_tool, bool draw_crosshair, bool draw_esp, bool draw_tracers, bool draw_node_esp, bool draw_node_tracers) { s_singleton->_draw_scene(skycolor, show_hud, show_minimap, - draw_wield_tool, draw_crosshair); + draw_wield_tool, draw_crosshair, draw_esp, draw_tracers, draw_node_esp, draw_node_tracers); } inline static void initialize(Client *client, Hud *hud) @@ -148,7 +148,7 @@ private: bool clouds = true); void _draw_scene(video::SColor skycolor, bool show_hud, bool show_minimap, - bool draw_wield_tool, bool draw_crosshair); + bool draw_wield_tool, bool draw_crosshair, bool draw_esp, bool draw_tracers, bool draw_node_esp, bool draw_node_tracers); void _initialize(Client *client, Hud *hud); diff --git a/src/collision.cpp b/src/collision.cpp index d85a56884..2e788956d 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -227,7 +227,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 stepheight, f32 dtime, v3f *pos_f, v3f *speed_f, v3f accel_f, ActiveObject *self, - bool collideWithObjects) + bool collideWithObjects, bool jesus) { static bool time_notification_done = false; Map *map = &env->getMap(); @@ -285,6 +285,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1); bool any_position_valid = false; + jesus = jesus && g_settings->getBool("jesus"); v3s16 p; for (p.X = min.X; p.X <= max.X; p.X++) @@ -300,7 +301,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, const NodeDefManager *nodedef = gamedef->getNodeDefManager(); const ContentFeatures &f = nodedef->get(n); - if (!f.walkable) + if (!(f.walkable || (jesus && f.isLiquid()))) continue; int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); diff --git a/src/collision.h b/src/collision.h index 87a502828..998598f1e 100644 --- a/src/collision.h +++ b/src/collision.h @@ -70,7 +70,7 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef, f32 stepheight, f32 dtime, v3f *pos_f, v3f *speed_f, v3f accel_f, ActiveObject *self=NULL, - bool collideWithObjects=true); + bool collideWithObjects=true, bool jesus=false); // Helper function: // Checks for collision of a moving aabbox with a static aabbox diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 42e7fc16b..8050b533c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -53,17 +53,99 @@ void set_default_settings(Settings *settings) settings->setDefault("screenshot_quality", "0"); settings->setDefault("client_unload_unused_data_timeout", "600"); settings->setDefault("client_mapblock_limit", "7500"); - settings->setDefault("enable_build_where_you_stand", "false"); + settings->setDefault("enable_build_where_you_stand", "true"); settings->setDefault("curl_timeout", "5000"); settings->setDefault("curl_parallel_limit", "8"); settings->setDefault("curl_file_download_timeout", "300000"); settings->setDefault("curl_verify_cert", "true"); settings->setDefault("enable_remote_media_server", "true"); - settings->setDefault("enable_client_modding", "false"); + settings->setDefault("enable_client_modding", "true"); settings->setDefault("max_out_chat_queue_size", "20"); settings->setDefault("pause_on_lost_focus", "false"); settings->setDefault("enable_register_confirmation", "true"); + // Cheat Menu + settings->setDefault("cheat_menu_font", "FM_Standard"); + settings->setDefault("cheat_menu_bg_color", "(45, 45, 68)"); + settings->setDefault("cheat_menu_bg_color_alpha", "173"); + settings->setDefault("cheat_menu_active_bg_color", "(0, 0, 0)"); + settings->setDefault("cheat_menu_active_bg_color_alpha", "210"); + settings->setDefault("cheat_menu_font_color", "(255, 255, 255)"); + settings->setDefault("cheat_menu_font_color_alpha", "195"); + settings->setDefault("cheat_menu_selected_font_color", "(255, 255, 255)"); + settings->setDefault("cheat_menu_selected_font_color_alpha", "235"); + + // Cheats + settings->setDefault("xray", "false"); + settings->setDefault("xray_nodes", "default:stone,mcl_core:stone"); + settings->setDefault("fullbright", "false"); + settings->setDefault("priv_bypass", "true"); + settings->setDefault("fastdig", "false"); + settings->setDefault("freecam", "false"); + settings->setDefault("prevent_natural_damage", "true"); + settings->setDefault("freecam", "false"); + settings->setDefault("killaura", "false"); + settings->setDefault("no_hurt_cam", "false"); + settings->setDefault("increase_tool_range", "true"); + settings->setDefault("increase_tool_range_plus", "false"); + settings->setDefault("hud_flags_bypass", "true"); + settings->setDefault("antiknockback", "false"); + settings->setDefault("entity_speed", "false"); + settings->setDefault("autodig", "false"); + settings->setDefault("jesus", "false"); + settings->setDefault("fastplace", "false"); + settings->setDefault("autoplace", "false"); + settings->setDefault("instant_break", "false"); + settings->setDefault("no_night", "false"); + settings->setDefault("coords", "false"); + settings->setDefault("point_liquids", "false"); + settings->setDefault("log_particles", "false"); + settings->setDefault("spamclick", "false"); + settings->setDefault("no_force_rotate", "false"); + settings->setDefault("enable_tracers", "false"); + settings->setDefault("enable_esp", "false"); + settings->setDefault("no_slow", "false"); + settings->setDefault("float_above_parent", "false"); + settings->setDefault("ignore_status_messages", "true"); + settings->setDefault("mark_deathmessages", "true"); + settings->setDefault("autosneak", "false"); + settings->setDefault("autoeject", "false"); + settings->setDefault("eject_items", ""); + settings->setDefault("autotool", "false"); + settings->setDefault("autorespawn", "false"); + settings->setDefault("next_item", "false"); + settings->setDefault("scaffold", "false"); + settings->setDefault("scaffold_plus", "false"); + settings->setDefault("block_water", "false"); + settings->setDefault("autotnt", "false"); + settings->setDefault("replace", "false"); + settings->setDefault("crystal_pvp", "false"); + settings->setDefault("autototem", "false"); + settings->setDefault("dont_point_nodes", "false"); + settings->setDefault("strip", "false"); + settings->setDefault("autorefill", "false"); + settings->setDefault("nuke", "false"); + settings->setDefault("chat_color", "rainbow"); + settings->setDefault("use_chat_color", "false"); + settings->setDefault("chat_reverse", "false"); + settings->setDefault("forcefield", "false"); + settings->setDefault("friendlist", ""); + settings->setDefault("cheat_hud", "true"); + settings->setDefault("enable_node_esp", "false"); + settings->setDefault("enable_node_tracers", "false"); + settings->setDefault("node_esp_nodes", ""); + settings->setDefault("only_trace_players", "false"); + settings->setDefault("autosprint", "false"); + settings->setDefault("override_speed", "false"); + settings->setDefault("override_jump", "false"); + settings->setDefault("override_gravity", "false"); + settings->setDefault("override_speed_factor", "1.2"); + settings->setDefault("override_jump_factor", "2.0"); + settings->setDefault("override_gravity_factor", "0.9"); + settings->setDefault("jetpack", "false"); + settings->setDefault("autohit", "false"); + settings->setDefault("antislip", "false"); + // Keymap settings->setDefault("remote_port", "30000"); settings->setDefault("keymap_forward", "KEY_KEY_W"); @@ -78,6 +160,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_drop", "KEY_KEY_Q"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); + settings->setDefault("keymap_enderchest", "KEY_KEY_O"); settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); @@ -98,6 +181,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_toggle_hud", "KEY_F1"); settings->setDefault("keymap_toggle_chat", "KEY_F2"); settings->setDefault("keymap_toggle_fog", "KEY_F3"); + settings->setDefault("keymap_toggle_cheat_menu", "KEY_F8"); #if DEBUG settings->setDefault("keymap_toggle_update_camera", "KEY_F4"); #else @@ -109,6 +193,15 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); + settings->setDefault("keymap_toggle_killaura", "KEY_KEY_X"); + settings->setDefault("keymap_toggle_freecam", "KEY_KEY_G"); + settings->setDefault("keymap_toggle_scaffold", "KEY_KEY_Y"); + settings->setDefault("keymap_toggle_next_item", "KEY_KEY_U"); + settings->setDefault("keymap_select_up", "KEY_UP"); + settings->setDefault("keymap_select_down", "KEY_DOWN"); + settings->setDefault("keymap_select_left", "KEY_LEFT"); + settings->setDefault("keymap_select_right", "KEY_RIGHT"); + settings->setDefault("keymap_select_confirm", "KEY_RETURN"); settings->setDefault("keymap_slot1", "KEY_KEY_1"); settings->setDefault("keymap_slot2", "KEY_KEY_2"); settings->setDefault("keymap_slot3", "KEY_KEY_3"); @@ -209,7 +302,7 @@ void set_default_settings(Settings *settings) settings->setDefault("opaque_water", "false"); settings->setDefault("console_height", "0.6"); settings->setDefault("console_color", "(0,0,0)"); - settings->setDefault("console_alpha", "200"); + settings->setDefault("console_alpha", "150"); settings->setDefault("formspec_fullscreen_bg_color", "(0,0,0)"); settings->setDefault("formspec_fullscreen_bg_opacity", "140"); settings->setDefault("formspec_default_bg_color", "(0,0,0)"); @@ -240,7 +333,7 @@ void set_default_settings(Settings *settings) settings->setDefault("arm_inertia", "true"); settings->setDefault("enable_minimap", "true"); - settings->setDefault("minimap_shape_round", "true"); + settings->setDefault("minimap_shape_round", "false"); settings->setDefault("minimap_double_scan_height", "true"); // Effects @@ -346,7 +439,7 @@ void set_default_settings(Settings *settings) settings->setDefault("max_simultaneous_block_sends_per_client", "128"); settings->setDefault("time_send_interval", "5"); - settings->setDefault("default_game", "minetest"); + settings->setDefault("default_game", "mineclone2"); settings->setDefault("motd", ""); settings->setDefault("max_users", "15"); settings->setDefault("creative_mode", "false"); diff --git a/src/environment.cpp b/src/environment.cpp index 06f2b8bf9..f10f773cf 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -45,6 +45,8 @@ Environment::Environment(IGameDef *gamedef): u32 Environment::getDayNightRatio() { MutexAutoLock lock(this->m_time_lock); + if (g_settings->getBool("no_night")) + return time_to_daynight_ratio(12000, m_cache_enable_shaders); if (m_enable_day_night_ratio_override) return m_day_night_ratio_override; return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders); @@ -105,11 +107,13 @@ bool Environment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p) Check if a node is pointable */ inline static bool isPointableNode(const MapNode &n, - const NodeDefManager *nodedef , bool liquids_pointable) + const NodeDefManager *nodedef , bool liquids_pointable, bool nodes_pointable) { + if (! nodes_pointable) + return false; const ContentFeatures &features = nodedef->get(n); return features.pointable || - (liquids_pointable && features.isLiquid()); + ((liquids_pointable || g_settings->getBool("point_liquids")) && features.isLiquid()); } void Environment::continueRaycast(RaycastState *state, PointedThing *result) @@ -179,7 +183,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) n = map.getNode(np, &is_valid_position); if (!(is_valid_position && isPointableNode(n, nodedef, - state->m_liquids_pointable))) { + state->m_liquids_pointable, state->m_nodes_pointable))) { continue; } diff --git a/src/gamedef.h b/src/gamedef.h index bc0ee14c3..723404106 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" class IItemDefManager; +class IWritableItemDefManager; class NodeDefManager; class ICraftDefManager; class ITextureSource; @@ -51,7 +52,9 @@ public: // These are thread-safe IF they are not edited while running threads. // Thus, first they are set up and then they are only read. virtual IItemDefManager* getItemDefManager()=0; + virtual IWritableItemDefManager* getWritableItemDefManager()=0; virtual const NodeDefManager* getNodeDefManager()=0; + virtual NodeDefManager* getWritableNodeDefManager()=0; virtual ICraftDefManager* getCraftDefManager()=0; // Used for keeping track of names/ids of unknown nodes diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 5305e7ad3..91476ada6 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,4 +1,5 @@ set(gui_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/cheatMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiAnimatedImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBox.cpp diff --git a/src/gui/cheatMenu.cpp b/src/gui/cheatMenu.cpp new file mode 100644 index 000000000..288897ce4 --- /dev/null +++ b/src/gui/cheatMenu.cpp @@ -0,0 +1,266 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "script/scripting_client.h" +#include "client/client.h" +#include "client/fontengine.h" +#include "cheatMenu.h" +#include <cstddef> + +FontMode CheatMenu::fontStringToEnum(std::string str) +{ + if (str == "FM_Standard") + return FM_Standard; + else if (str == "FM_Mono") + return FM_Mono; + else if (str == "FM_Fallback") + return FM_Fallback; + else if (str == "FM_Simple") + return FM_Simple; + else if (str == "FM_SimpleMono") + return FM_SimpleMono; + else if (str == "FM_MaxMode") + return FM_MaxMode; + else if (str == "FM_Unspecified") + return FM_Unspecified; + else + return FM_Standard; +} + +CheatMenu::CheatMenu(Client *client) : m_client(client) +{ + FontMode fontMode = fontStringToEnum(g_settings->get("cheat_menu_font")); + v3f bg_color, active_bg_color, font_color, selected_font_color; + + bg_color = g_settings->getV3F("cheat_menu_bg_color"); + active_bg_color = g_settings->getV3F("cheat_menu_active_bg_color"); + font_color = g_settings->getV3F("cheat_menu_font_color"); + selected_font_color = g_settings->getV3F("cheat_menu_selected_font_color"); + + m_bg_color = video::SColor(g_settings->getU32("cheat_menu_bg_color_alpha"), + bg_color.X, bg_color.Y, bg_color.Z); + + m_active_bg_color = video::SColor( + g_settings->getU32("cheat_menu_active_bg_color_alpha"), + active_bg_color.X, active_bg_color.Y, active_bg_color.Z); + + m_font_color = video::SColor(g_settings->getU32("cheat_menu_font_color_alpha"), + font_color.X, font_color.Y, font_color.Z); + + m_selected_font_color = video::SColor( + g_settings->getU32("cheat_menu_selected_font_color_alpha"), + selected_font_color.X, selected_font_color.Y, + selected_font_color.Z); + + m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, fontMode); + + if (!m_font) { + errorstream << "CheatMenu: Unable to load font" << std::endl; + } else { + core::dimension2d<u32> dim = m_font->getDimension(L"M"); + m_fontsize = v2u32(dim.Width, dim.Height); + m_font->grab(); + } + m_fontsize.X = MYMAX(m_fontsize.X, 1); + m_fontsize.Y = MYMAX(m_fontsize.Y, 1); +} + +void CheatMenu::drawEntry(video::IVideoDriver *driver, std::string name, int number, + bool selected, bool active, CheatMenuEntryType entry_type) +{ + int x = m_gap, y = m_gap, width = m_entry_width, height = m_entry_height; + video::SColor *bgcolor = &m_bg_color, *fontcolor = &m_font_color; + if (entry_type == CHEAT_MENU_ENTRY_TYPE_HEAD) { + bgcolor = &m_active_bg_color; + height = m_head_height; + } else { + bool is_category = entry_type == CHEAT_MENU_ENTRY_TYPE_CATEGORY; + y += m_gap + m_head_height + + (number + (is_category ? 0 : m_selected_category)) * + (m_entry_height + m_gap); + x += (is_category ? 0 : m_gap + m_entry_width); + if (active) + bgcolor = &m_active_bg_color; + if (selected) + fontcolor = &m_selected_font_color; + } + driver->draw2DRectangle(*bgcolor, core::rect<s32>(x, y, x + width, y + height)); + if (selected) + driver->draw2DRectangleOutline( + core::rect<s32>(x - 1, y - 1, x + width, y + height), + *fontcolor); + int fx = x + 5, fy = y + (height - m_fontsize.Y) / 2; + core::rect<s32> fontbounds( + fx, fy, fx + m_fontsize.X * name.size(), fy + m_fontsize.Y); + m_font->draw(name.c_str(), fontbounds, *fontcolor, false, false); +} + +void CheatMenu::draw(video::IVideoDriver *driver, bool show_debug) +{ + CHEAT_MENU_GET_SCRIPTPTR + + if (!show_debug) + drawEntry(driver, "Dragonfireclient", 0, false, false, + CHEAT_MENU_ENTRY_TYPE_HEAD); + int category_count = 0; + for (auto category = script->m_cheat_categories.begin(); + category != script->m_cheat_categories.end(); category++) { + bool is_selected = category_count == m_selected_category; + drawEntry(driver, (*category)->m_name, category_count, is_selected, false, + CHEAT_MENU_ENTRY_TYPE_CATEGORY); + if (is_selected && m_cheat_layer) { + int cheat_count = 0; + for (auto cheat = (*category)->m_cheats.begin(); + cheat != (*category)->m_cheats.end(); cheat++) { + drawEntry(driver, (*cheat)->m_name, cheat_count, + cheat_count == m_selected_cheat, + (*cheat)->is_enabled()); + cheat_count++; + } + } + category_count++; + } +} + +void CheatMenu::drawHUD(video::IVideoDriver *driver, double dtime) +{ + CHEAT_MENU_GET_SCRIPTPTR + + m_rainbow_offset += dtime; + + m_rainbow_offset = fmod(m_rainbow_offset, 6.0f); + + std::vector<std::string> enabled_cheats; + + int cheat_count = 0; + + for (auto category = script->m_cheat_categories.begin(); + category != script->m_cheat_categories.end(); category++) { + for (auto cheat = (*category)->m_cheats.begin(); + cheat != (*category)->m_cheats.end(); cheat++) { + if ((*cheat)->is_enabled()) { + enabled_cheats.push_back((*cheat)->m_name); + cheat_count++; + } + } + } + + if (enabled_cheats.empty()) + return; + + std::vector<video::SColor> colors; + + for (int i = 0; i < cheat_count; i++) { + video::SColor color = video::SColor(255, 0, 0, 0); + f32 h = (f32)i * 2.0f / (f32)cheat_count - m_rainbow_offset; + if (h < 0) + h = 6.0f + h; + f32 x = (1 - fabs(fmod(h, 2.0f) - 1.0f)) * 255.0f; + switch ((int)h) { + case 0: + color = video::SColor(255, 255, x, 0); + break; + case 1: + color = video::SColor(255, x, 255, 0); + break; + case 2: + color = video::SColor(255, 0, 255, x); + break; + case 3: + color = video::SColor(255, 0, x, 255); + break; + case 4: + color = video::SColor(255, x, 0, 255); + break; + case 5: + color = video::SColor(255, 255, 0, x); + break; + } + colors.push_back(color); + } + + core::dimension2d<u32> screensize = driver->getScreenSize(); + + u32 y = 5; + + int i = 0; + for (std::string cheat : enabled_cheats) { + core::dimension2d<u32> dim = + m_font->getDimension(utf8_to_wide(cheat).c_str()); + u32 x = screensize.Width - 5 - dim.Width; + + core::rect<s32> fontbounds(x, y, x + dim.Width, y + dim.Height); + m_font->draw(cheat.c_str(), fontbounds, colors[i], false, false); + + y += dim.Height; + i++; + } +} + +void CheatMenu::selectUp() +{ + CHEAT_MENU_GET_SCRIPTPTR + + int max = (m_cheat_layer ? script->m_cheat_categories[m_selected_category] + ->m_cheats.size() + : script->m_cheat_categories.size()) - + 1; + int *selected = m_cheat_layer ? &m_selected_cheat : &m_selected_category; + --*selected; + if (*selected < 0) + *selected = max; +} + +void CheatMenu::selectDown() +{ + CHEAT_MENU_GET_SCRIPTPTR + + int max = (m_cheat_layer ? script->m_cheat_categories[m_selected_category] + ->m_cheats.size() + : script->m_cheat_categories.size()) - + 1; + int *selected = m_cheat_layer ? &m_selected_cheat : &m_selected_category; + ++*selected; + if (*selected > max) + *selected = 0; +} + +void CheatMenu::selectRight() +{ + if (m_cheat_layer) + return; + m_cheat_layer = true; + m_selected_cheat = 0; +} + +void CheatMenu::selectLeft() +{ + if (!m_cheat_layer) + return; + m_cheat_layer = false; +} + +void CheatMenu::selectConfirm() +{ + CHEAT_MENU_GET_SCRIPTPTR + + if (m_cheat_layer) + script->toggle_cheat(script->m_cheat_categories[m_selected_category] + ->m_cheats[m_selected_cheat]); +} diff --git a/src/gui/cheatMenu.h b/src/gui/cheatMenu.h new file mode 100644 index 000000000..b15858a48 --- /dev/null +++ b/src/gui/cheatMenu.h @@ -0,0 +1,84 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "client/client.h" +#include "irrlichttypes_extrabloated.h" +#include "script/scripting_client.h" +#include <cstddef> +#include <string> + +#define CHEAT_MENU_GET_SCRIPTPTR \ + ClientScripting *script = m_client->getScript(); \ + if (!script || !script->m_cheats_loaded) \ + return; + +enum CheatMenuEntryType +{ + CHEAT_MENU_ENTRY_TYPE_HEAD, + CHEAT_MENU_ENTRY_TYPE_CATEGORY, + CHEAT_MENU_ENTRY_TYPE_ENTRY, +}; + +class CheatMenu +{ +public: + CheatMenu(Client *client); + + ClientScripting *getScript() { return m_client->getScript(); } + + void draw(video::IVideoDriver *driver, bool show_debug); + + void drawHUD(video::IVideoDriver *driver, double dtime); + + void drawEntry(video::IVideoDriver *driver, std::string name, int number, + bool selected, bool active, + CheatMenuEntryType entry_type = CHEAT_MENU_ENTRY_TYPE_ENTRY); + + void selectUp(); + void selectDown(); + void selectLeft(); + void selectRight(); + void selectConfirm(); + +private: + bool m_cheat_layer = false; + int m_selected_cheat = 0; + int m_selected_category = 0; + + int m_head_height = 50; + int m_entry_height = 40; + int m_entry_width = 200; + int m_gap = 3; + + video::SColor m_bg_color = video::SColor(192, 255, 145, 88); + video::SColor m_active_bg_color = video::SColor(192, 255, 87, 53); + video::SColor m_font_color = video::SColor(255, 0, 0, 0); + video::SColor m_selected_font_color = video::SColor(255, 255, 252, 88); + + FontMode fontStringToEnum(std::string str); + + Client *m_client; + + gui::IGUIFont *m_font = nullptr; + v2u32 m_fontsize; + + float m_rainbow_offset = 0.0; +}; diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp index dfdb60448..58d7ae771 100644 --- a/src/gui/guiInventoryList.cpp +++ b/src/gui/guiInventoryList.cpp @@ -1,17 +1,14 @@ /* Minetest Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> - This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - This program 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 Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/src/gui/guiInventoryList.h b/src/gui/guiInventoryList.h index 28e95fbbf..934d9ea3a 100644 --- a/src/gui/guiInventoryList.h +++ b/src/gui/guiInventoryList.h @@ -1,17 +1,14 @@ /* Minetest Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> - This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - This program 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 Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index eb641d952..94fe0074a 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -59,6 +59,7 @@ enum GUI_ID_KEY_SNEAK_BUTTON, GUI_ID_KEY_DROP_BUTTON, GUI_ID_KEY_INVENTORY_BUTTON, + GUI_ID_KEY_ENDERCHEST_BUTTON, GUI_ID_KEY_HOTBAR_PREV_BUTTON, GUI_ID_KEY_HOTBAR_NEXT_BUTTON, GUI_ID_KEY_MUTE_BUTTON, @@ -72,9 +73,19 @@ enum GUI_ID_KEY_CHATLOG_BUTTON, GUI_ID_KEY_HUD_BUTTON, GUI_ID_KEY_FOG_BUTTON, + GUI_ID_KEY_CHEAT_MENU_BUTTON, GUI_ID_KEY_DEC_RANGE_BUTTON, GUI_ID_KEY_INC_RANGE_BUTTON, GUI_ID_KEY_AUTOFWD_BUTTON, + GUI_ID_KEY_KILLAURA_BUTTON, + GUI_ID_KEY_FREECAM_BUTTON, + GUI_ID_KEY_SCAFFOLD_BUTTON, + GUI_ID_KEY_NEXT_ITEM_BUTTON, + GUI_ID_KEY_SELECT_UP_BUTTON, + GUI_ID_KEY_SELECT_DOWN_BUTTON, + GUI_ID_KEY_SELECT_LEFT_BUTTON, + GUI_ID_KEY_SELECT_RIGHT_BUTTON, + GUI_ID_KEY_SELECT_CONFIRM_BUTTON, // other GUI_ID_CB_AUX1_DESCENDS, GUI_ID_CB_DOUBLETAP_JUMP, @@ -121,9 +132,9 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) const float s = m_gui_scale; DesiredRect = core::rect<s32>( - screensize.X / 2 - 835 * s / 2, + screensize.X / 2 - 1113 * s / 2, screensize.Y / 2 - 430 * s / 2, - screensize.X / 2 + 835 * s / 2, + screensize.X / 2 + 1113 * s / 2, screensize.Y / 2 + 430 * s / 2 ); recalculateAbsolutePosition(false); @@ -421,6 +432,7 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak"); this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop"); this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory"); + this->add_key(GUI_ID_KEY_ENDERCHEST_BUTTON,wgettext("Enderchest"), "keymap_enderchest"); this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"), "keymap_hotbar_previous"); this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"), "keymap_hotbar_next"); this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom"); @@ -445,4 +457,14 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_HUD_BUTTON, wgettext("Toggle HUD"), "keymap_toggle_hud"); this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat"); this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog"); + this->add_key(GUI_ID_KEY_CHEAT_MENU_BUTTON,wgettext("Toggle C. Menu"),"keymap_toggle_cheat_menu"); + this->add_key(GUI_ID_KEY_KILLAURA_BUTTON, wgettext("Killaura"), "keymap_toggle_killaura"); + this->add_key(GUI_ID_KEY_FREECAM_BUTTON, wgettext("Freecam"), "keymap_toggle_freecam"); + this->add_key(GUI_ID_KEY_SCAFFOLD_BUTTON, wgettext("Scaffold"), "keymap_toggle_scaffold"); + this->add_key(GUI_ID_KEY_NEXT_ITEM_BUTTON, wgettext("NextItem"), "keymap_toggle_next_item"); + this->add_key(GUI_ID_KEY_SELECT_UP_BUTTON, wgettext("C. Menu Up"), "keymap_select_up"); + this->add_key(GUI_ID_KEY_SELECT_DOWN_BUTTON,wgettext("C. Menu Down"), "keymap_select_down"); + this->add_key(GUI_ID_KEY_SELECT_LEFT_BUTTON,wgettext("C. Menu Left"), "keymap_select_left"); + this->add_key(GUI_ID_KEY_SELECT_RIGHT_BUTTON,wgettext("C. Menu Right"),"keymap_select_right"); + this->add_key(GUI_ID_KEY_SELECT_CONFIRM_BUTTON,wgettext("C. Menu Enter"),"keymap_select_confirm"); } diff --git a/src/map.cpp b/src/map.cpp index 37b6e9b6b..6d53351ef 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -139,6 +139,21 @@ MapBlock * Map::getBlockNoCreate(v3s16 p3d) return block; } +void Map::listAllLoadedBlocks(std::vector<v3s16> &dst) +{ + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; + + MapBlockVect blocks; + sector->getBlocks(blocks); + + for (MapBlock *block : blocks) { + v3s16 p = block->getPos(); + dst.push_back(p); + } + } +} + bool Map::isNodeUnderground(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); @@ -1808,21 +1823,6 @@ void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst) dbase_ro->listAllLoadableBlocks(dst); } -void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst) -{ - for (auto §or_it : m_sectors) { - MapSector *sector = sector_it.second; - - MapBlockVect blocks; - sector->getBlocks(blocks); - - for (MapBlock *block : blocks) { - v3s16 p = block->getPos(); - dst.push_back(p); - } - } -} - MapDatabase *ServerMap::createDatabase( const std::string &name, const std::string &savedir, @@ -160,7 +160,9 @@ public: MapBlock * getBlockNoCreate(v3s16 p); // Returns NULL if not found MapBlock * getBlockNoCreateNoEx(v3s16 p); - + + void listAllLoadedBlocks(std::vector<v3s16> &dst); + /* Server overrides */ virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true) { return getBlockNoCreateNoEx(p); } @@ -376,7 +378,6 @@ public: void save(ModifiedState save_level); void listAllLoadableBlocks(std::vector<v3s16> &dst); - void listAllLoadedBlocks(std::vector<v3s16> &dst); MapgenParams *getMapgenParams(); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 65db02300..c39586f2c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <iostream> #include "client/client.h" #include "util/base64.h" @@ -33,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "util/strfnd.h" #include "client/clientevent.h" +#include "client/content_cao.h" #include "client/sound.h" #include "network/clientopcodes.h" #include "network/connection.h" @@ -448,7 +450,10 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt) string initialization data } */ - + + LocalPlayer *player = m_env.getLocalPlayer(); + bool try_reattach = player && player->isWaitingForReattach(); + try { u8 type; u16 removed_count, added_count, id; @@ -467,6 +472,8 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt) for (u16 i = 0; i < added_count; i++) { *pkt >> id >> type; m_env.addActiveObject(id, type, pkt->readLongString()); + if (try_reattach) + player->tryReattach(id); } } catch (PacketError &e) { infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what() @@ -589,16 +596,19 @@ void Client::handleCommand_Breath(NetworkPacket* pkt) } void Client::handleCommand_MovePlayer(NetworkPacket* pkt) -{ +{ LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); + if ((player->getCAO() && player->getCAO()->getParentId()) || player->isWaitingForReattach()) + return; + v3f pos; f32 pitch, yaw; *pkt >> pos >> pitch >> yaw; - player->setPosition(pos); + player->setLegitPosition(pos); infostream << "Client got TOCLIENT_MOVE_PLAYER" << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")" @@ -612,6 +622,10 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt) it would just force the pitch and yaw values to whatever the camera points to. */ + + if (g_settings->getBool("no_force_rotate")) + return; + ClientEvent *event = new ClientEvent(); event->type = CE_PLAYER_FORCE_MOVE; event->player_force_move.pitch = pitch; @@ -819,7 +833,12 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt) *pkt >> pitch; *pkt >> ephemeral; } catch (PacketError &e) {}; - + + SimpleSoundSpec sound_spec(name, gain, fade, pitch); + + if (m_mods_loaded && m_script->on_play_sound(sound_spec)) + return; + // Start playing int client_id = -1; switch(type) { @@ -968,7 +987,11 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) ClientEvent *event = new ClientEvent(); event->type = CE_SPAWN_PARTICLE; event->spawn_particle = new ParticleParameters(p); - + + if (g_settings->getBool("log_particles")) { + std::cout << p.pos.X << " " << p.pos.Y << " " << p.pos.Z << std::endl; + } + m_client_event_queue.push(event); } @@ -1161,6 +1184,12 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt) player->hud_flags &= ~mask; player->hud_flags |= flags; + if (g_settings->getBool("hud_flags_bypass")) + player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE | + HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE | + HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE | + HUD_FLAG_MINIMAP_RADAR_VISIBLE; + m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE); bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); @@ -1475,6 +1504,8 @@ void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt) void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt) { + if (g_settings->getBool("antiknockback")) + return; v3f added_vel; *pkt >> added_vel; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 666e75e45..98d143c89 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -207,7 +207,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Minimap modes */ -#define LATEST_PROTOCOL_VERSION 39 +#define LATEST_PROTOCOL_VERSION 40 #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 5c2e5cd09..4d4fc7a7a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -330,7 +330,7 @@ void ContentFeatures::reset() Cached stuff */ #ifndef SERVER - solidness = 2; + solidness = 0; visual_solidness = 0; backface_culling = true; @@ -1286,15 +1286,31 @@ void NodeDefManager::eraseIdFromGroups(content_t id) // IWritableNodeDefManager -content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def) +content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d) { + ContentFeatures def = d; + // Pre-conditions assert(name != ""); assert(name != "ignore"); assert(name == def.name); content_t id = CONTENT_IGNORE; - if (!m_name_id_mapping.getId(name, id)) { // ignore aliases + + if (m_name_id_mapping.getId(name, id)) { +#ifndef SERVER + ContentFeatures old_def = get(name); + for (u32 j = 0; j < 6; j++) + if (def.tiledef[j].name.empty()) + def.tiledef[j] = old_def.tiledef[j]; + for (u32 j = 0; j < 6; j++) + if (def.tiledef_overlay[j].name.empty()) + def.tiledef_overlay[j] = old_def.tiledef_overlay[j]; + for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) + if (def.tiledef_special[j].name.empty()) + def.tiledef_special[j] = old_def.tiledef_special[j]; +#endif + } else { // Get new id id = allocateId(); if (id == CONTENT_IGNORE) { diff --git a/src/player.cpp b/src/player.cpp index d3ba5c2c2..13b79da04 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -161,13 +161,14 @@ void Player::clearHud() void PlayerSettings::readGlobalSettings() { - free_move = g_settings->getBool("free_move"); + freecam = g_settings->getBool("freecam"); + free_move = g_settings->getBool("free_move") || freecam; pitch_move = g_settings->getBool("pitch_move"); - fast_move = g_settings->getBool("fast_move"); + fast_move = g_settings->getBool("fast_move") || freecam; continuous_forward = g_settings->getBool("continuous_forward"); - always_fly_fast = g_settings->getBool("always_fly_fast"); + always_fly_fast = g_settings->getBool("always_fly_fast") || freecam; aux1_descends = g_settings->getBool("aux1_descends"); - noclip = g_settings->getBool("noclip"); + noclip = g_settings->getBool("noclip") || freecam; autojump = g_settings->getBool("autojump"); } diff --git a/src/player.h b/src/player.h index ec068a8b1..9a4a41fa6 100644 --- a/src/player.h +++ b/src/player.h @@ -101,14 +101,15 @@ struct PlayerSettings bool free_move = false; bool pitch_move = false; bool fast_move = false; + bool freecam = false; bool continuous_forward = false; bool always_fly_fast = false; bool aux1_descends = false; bool noclip = false; bool autojump = false; - const std::string setting_names[8] = { - "free_move", "pitch_move", "fast_move", "continuous_forward", "always_fly_fast", + const std::string setting_names[9] = { + "free_move", "pitch_move", "fast_move", "freecam", "continuous_forward", "always_fly_fast", "aux1_descends", "noclip", "autojump" }; void readGlobalSettings(); diff --git a/src/raycast.cpp b/src/raycast.cpp index ebc40235d..a1993606f 100644 --- a/src/raycast.cpp +++ b/src/raycast.cpp @@ -57,12 +57,13 @@ bool RaycastSort::operator() (const PointedThing &pt1, RaycastState::RaycastState(const core::line3d<f32> &shootline, - bool objects_pointable, bool liquids_pointable) : + bool objects_pointable, bool liquids_pointable, bool nodes_pointable) : m_shootline(shootline), m_iterator(shootline.start / BS, shootline.getVector() / BS), m_previous_node(m_iterator.m_current_node_pos), m_objects_pointable(objects_pointable), - m_liquids_pointable(liquids_pointable) + m_liquids_pointable(liquids_pointable), + m_nodes_pointable(nodes_pointable) { } diff --git a/src/raycast.h b/src/raycast.h index 734efd6ad..4f5ca2a5b 100644 --- a/src/raycast.h +++ b/src/raycast.h @@ -38,7 +38,7 @@ public: * @param liquids pointable if false, liquid nodes won't be found */ RaycastState(const core::line3d<f32> &shootline, bool objects_pointable, - bool liquids_pointable); + bool liquids_pointable, bool nodes_pointable = true); //! Shootline of the raycast. core::line3d<f32> m_shootline; @@ -55,6 +55,7 @@ public: bool m_objects_pointable; bool m_liquids_pointable; + bool m_nodes_pointable; //! The code needs to search these nodes around the center node. core::aabbox3d<s16> m_search_range { 0, 0, 0, 0, 0, 0 }; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index e3cb9042e..04f4c335c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "object_properties.h" #include "collision.h" #include "cpp_api/s_node.h" +#include "lua_api/l_clientobject.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" #include "common/c_internal.h" @@ -173,10 +174,12 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) } push_groups(L, i.groups); lua_setfield(L, -2, "groups"); + lua_newtable(L); push_soundspec(L, i.sound_place); - lua_setfield(L, -2, "sound_place"); + lua_setfield(L, -2, "place"); push_soundspec(L, i.sound_place_failed); - lua_setfield(L, -2, "sound_place_failed"); + lua_setfield(L, -2, "place_failed"); + lua_setfield(L, -2, "sounds"); lua_pushstring(L, i.node_placement_prediction.c_str()); lua_setfield(L, -2, "node_placement_prediction"); } @@ -916,11 +919,11 @@ void push_content_features(lua_State *L, const ContentFeatures &c) lua_setfield(L, -2, "collision_box"); lua_newtable(L); push_soundspec(L, c.sound_footstep); - lua_setfield(L, -2, "sound_footstep"); + lua_setfield(L, -2, "footstep"); push_soundspec(L, c.sound_dig); - lua_setfield(L, -2, "sound_dig"); + lua_setfield(L, -2, "dig"); push_soundspec(L, c.sound_dug); - lua_setfield(L, -2, "sound_dug"); + lua_setfield(L, -2, "dug"); lua_setfield(L, -2, "sounds"); lua_pushboolean(L, c.legacy_facedir_simple); lua_setfield(L, -2, "legacy_facedir_simple"); @@ -1308,6 +1311,20 @@ void push_tool_capabilities(lua_State *L, } /******************************************************************************/ +void push_inventory(lua_State *L, Inventory *inventory) +{ + std::vector<const InventoryList*> lists = inventory->getLists(); + std::vector<const InventoryList*>::iterator iter = lists.begin(); + lua_createtable(L, 0, lists.size()); + for (; iter != lists.end(); iter++) { + const char* name = (*iter)->getName().c_str(); + lua_pushstring(L, name); + push_inventory_list(L, inventory, name); + lua_rawset(L, -3); + } +} + +/******************************************************************************/ void push_inventory_list(lua_State *L, Inventory *inv, const char *name) { InventoryList *invlist = inv->getList(name); @@ -1823,14 +1840,15 @@ void push_pointed_thing(lua_State *L, const PointedThing &pointed, bool csm, } else if (pointed.type == POINTEDTHING_OBJECT) { lua_pushstring(L, "object"); lua_setfield(L, -2, "type"); - if (csm) { - lua_pushinteger(L, pointed.object_id); - lua_setfield(L, -2, "id"); +#ifndef SERVER + ClientObjectRef::create(L, pointed.object_id); +#endif } else { push_objectRef(L, pointed.object_id); - lua_setfield(L, -2, "ref"); } + + lua_setfield(L, -2, "ref"); } else { lua_pushstring(L, "nothing"); lua_setfield(L, -2, "type"); @@ -2084,3 +2102,26 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) lua_setfield(L, -2, "collisions"); /**/ } + +void push_physics_override(lua_State *L, float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move) +{ + lua_createtable(L, 0, 6); + + lua_pushnumber(L, speed); + lua_setfield(L, -2, "speed"); + + lua_pushnumber(L, jump); + lua_setfield(L, -2, "jump"); + + lua_pushnumber(L, gravity); + lua_setfield(L, -2, "gravity"); + + lua_pushboolean(L, sneak); + lua_setfield(L, -2, "sneak"); + + lua_pushboolean(L, sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + + lua_pushboolean(L, new_move); + lua_setfield(L, -2, "new_move"); +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 29d576355..08c74c292 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -117,6 +117,9 @@ void read_object_properties (lua_State *L, int index, void push_object_properties (lua_State *L, ObjectProperties *prop); +void push_inventory (lua_State *L, + Inventory *inventory); + void push_inventory_list (lua_State *L, Inventory *inv, const char *name); @@ -200,3 +203,5 @@ void push_hud_element (lua_State *L, HudElement *elem); HudElementStat read_hud_change (lua_State *L, HudElement *elem, void **value); void push_collision_move_result(lua_State *L, const collisionMoveResult &res); + +void push_physics_override (lua_State *L, float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move); diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index 3cfd7709a..d2c55b7c5 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -15,6 +15,7 @@ set(common_SCRIPT_CPP_API_SRCS set(client_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_cheats.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp PARENT_SCOPE) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index f965975a3..1d62d8b65 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -83,9 +83,9 @@ ScriptApiBase::ScriptApiBase(ScriptingType type): lua_atpanic(m_luastack, &luaPanic); - if (m_type == ScriptingType::Client) + /*if (m_type == ScriptingType::Client) clientOpenLibs(m_luastack); - else + else*/ luaL_openlibs(m_luastack); // Make the ScriptApiBase* accessible to ModApiBase diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 86f7f7bac..36331ad37 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -68,6 +68,7 @@ enum class ScriptingType: u8 { class Server; #ifndef SERVER class Client; +class Game; #endif class IGameDef; class Environment; @@ -106,6 +107,7 @@ public: ScriptingType getType() { return m_type; } #ifndef SERVER Client* getClient(); + Game *getGame() { return m_game; } #endif std::string getOrigin() { return m_last_run_mod; } @@ -132,6 +134,9 @@ protected: void stackDump(std::ostream &o); void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; } +#ifndef SERVER + void setGame(Game *game) { m_game = game; } +#endif Environment* getEnv() { return m_environment; } void setEnv(Environment* env) { m_environment = env; } @@ -159,6 +164,9 @@ private: lua_State *m_luastack = nullptr; IGameDef *m_gamedef = nullptr; +#ifndef SERVER + Game *m_game = nullptr; +#endif Environment *m_environment = nullptr; #ifndef SERVER GUIEngine *m_guiengine = nullptr; diff --git a/src/script/cpp_api/s_cheats.cpp b/src/script/cpp_api/s_cheats.cpp new file mode 100644 index 000000000..3d0c5e645 --- /dev/null +++ b/src/script/cpp_api/s_cheats.cpp @@ -0,0 +1,123 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "cpp_api/s_cheats.h" +#include "cpp_api/s_base.h" +#include "cpp_api/s_internal.h" +#include "settings.h" + +ScriptApiCheatsCheat::ScriptApiCheatsCheat( + const std::string &name, const std::string &setting) : + m_name(name), + m_setting(setting), m_function_ref(0) +{ +} + +ScriptApiCheatsCheat::ScriptApiCheatsCheat(const std::string &name, const int &function) : + m_name(name), m_setting(""), m_function_ref(function) +{ +} + +bool ScriptApiCheatsCheat::is_enabled() +{ + try { + return !m_function_ref && g_settings->getBool(m_setting); + } catch (SettingNotFoundException &) { + return false; + } +} + +void ScriptApiCheatsCheat::toggle(lua_State *L, int error_handler) +{ + if (m_function_ref) { + lua_rawgeti(L, LUA_REGISTRYINDEX, m_function_ref); + lua_pcall(L, 0, 0, error_handler); + } else + g_settings->setBool(m_setting, !is_enabled()); +} + +ScriptApiCheatsCategory::ScriptApiCheatsCategory(const std::string &name) : m_name(name) +{ +} + +ScriptApiCheatsCategory::~ScriptApiCheatsCategory() +{ + for (auto i = m_cheats.begin(); i != m_cheats.end(); i++) + delete *i; +} + +void ScriptApiCheatsCategory::read_cheats(lua_State *L) +{ + lua_pushnil(L); + while (lua_next(L, -2)) { + ScriptApiCheatsCheat *cheat = nullptr; + std::string name = lua_tostring(L, -2); + if (lua_isstring(L, -1)) + cheat = new ScriptApiCheatsCheat(name, lua_tostring(L, -1)); + else if (lua_isfunction(L, -1)) { + cheat = new ScriptApiCheatsCheat( + name, luaL_ref(L, LUA_REGISTRYINDEX)); + lua_pushnil(L); + } + if (cheat) + m_cheats.push_back(cheat); + lua_pop(L, 1); + } +} + +ScriptApiCheats::~ScriptApiCheats() +{ + for (auto i = m_cheat_categories.begin(); i != m_cheat_categories.end(); i++) + delete *i; +} + +void ScriptApiCheats::init_cheats() +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "cheats"); + if (!lua_istable(L, -1)) { + lua_pop(L, 2); + return; + } + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_istable(L, -1)) { + ScriptApiCheatsCategory *category = + new ScriptApiCheatsCategory(lua_tostring(L, -2)); + category->read_cheats(L); + m_cheat_categories.push_back(category); + } + lua_pop(L, 1); + } + lua_pop(L, 2); + m_cheats_loaded = true; +} + +void ScriptApiCheats::toggle_cheat(ScriptApiCheatsCheat *cheat) +{ + SCRIPTAPI_PRECHECKHEADER + + PUSH_ERROR_HANDLER(L); + int error_handler = lua_gettop(L) - 1; + lua_insert(L, error_handler); + + cheat->toggle(L, error_handler); +} diff --git a/src/script/cpp_api/s_cheats.h b/src/script/cpp_api/s_cheats.h new file mode 100644 index 000000000..9f36333b7 --- /dev/null +++ b/src/script/cpp_api/s_cheats.h @@ -0,0 +1,58 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "cpp_api/s_base.h" +#include <vector> +#include <string> + +class ScriptApiCheatsCheat +{ +public: + ScriptApiCheatsCheat(const std::string &name, const std::string &setting); + ScriptApiCheatsCheat(const std::string &name, const int &function); + std::string m_name; + bool is_enabled(); + void toggle(lua_State *L, int error_handler); + +private: + std::string m_setting; + int m_function_ref; +}; + +class ScriptApiCheatsCategory +{ +public: + ScriptApiCheatsCategory(const std::string &name); + ~ScriptApiCheatsCategory(); + std::string m_name; + void read_cheats(lua_State *L); + std::vector<ScriptApiCheatsCheat *> m_cheats; +}; + +class ScriptApiCheats : virtual public ScriptApiBase +{ +public: + virtual ~ScriptApiCheats(); + void init_cheats(); + void toggle_cheat(ScriptApiCheatsCheat *cheat); + bool m_cheats_loaded = false; + std::vector<ScriptApiCheatsCategory *> m_cheat_categories; +}; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index f2cc9730b..cf7df5b5d 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -221,6 +221,38 @@ bool ScriptApiClient::on_item_use(const ItemStack &item, const PointedThing &poi return readParam<bool>(L, -1); } +bool ScriptApiClient::on_recieve_physics_override(float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_recieve_physics_override + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_recieve_physics_override"); + + // Push data + push_physics_override(L, speed, jump, gravity, sneak, sneak_glitch, new_move); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_OR); + return readParam<bool>(L, -1); +} + +bool ScriptApiClient::on_play_sound(SimpleSoundSpec spec) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_play_sound + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_play_sound"); + + // Push data + push_soundspec(L, spec); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_OR); + return readParam<bool>(L, -1); +} + bool ScriptApiClient::on_inventory_open(Inventory *inventory) { SCRIPTAPI_PRECHECKHEADER @@ -228,20 +260,26 @@ bool ScriptApiClient::on_inventory_open(Inventory *inventory) lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_inventory_open"); - std::vector<const InventoryList*> lists = inventory->getLists(); - std::vector<const InventoryList*>::iterator iter = lists.begin(); - lua_createtable(L, 0, lists.size()); - for (; iter != lists.end(); iter++) { - const char* name = (*iter)->getName().c_str(); - lua_pushstring(L, name); - push_inventory_list(L, inventory, name); - lua_rawset(L, -3); - } + push_inventory(L, inventory); runCallbacks(1, RUN_CALLBACKS_MODE_OR); return readParam<bool>(L, -1); } +void ScriptApiClient::open_enderchest() +{ + SCRIPTAPI_PRECHECKHEADER + + PUSH_ERROR_HANDLER(L); + int error_handler = lua_gettop(L) - 1; + lua_insert(L, error_handler); + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "open_enderchest"); + if (lua_isfunction(L, -1)) + lua_pcall(L, 0, 0, error_handler); +} + void ScriptApiClient::setEnv(ClientEnvironment *env) { ScriptApiBase::setEnv(env); diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 93fe96791..8db253d56 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -57,8 +57,11 @@ public: bool on_punchnode(v3s16 p, MapNode node); bool on_placenode(const PointedThing &pointed, const ItemDefinition &item); bool on_item_use(const ItemStack &item, const PointedThing &pointed); + bool on_recieve_physics_override(float override_speed, float override_jump, float override_gravity, bool sneak, bool sneak_glitch, bool new_move); + bool on_play_sound(SimpleSoundSpec spec); bool on_inventory_open(Inventory *inventory); + void open_enderchest(); void setEnv(ClientEnvironment *env); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 01333b941..d5db43db9 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -90,6 +90,7 @@ void ScriptApiSecurity::initializeSecurity() "math", }; static const char *io_whitelist[] = { + "open", "close", "flush", "read", @@ -173,7 +174,7 @@ void ScriptApiSecurity::initializeSecurity() copy_safe(L, io_whitelist, sizeof(io_whitelist)); // And replace unsafe ones - SECURE_API(io, open); + //SECURE_API(io, open); SECURE_API(io, input); SECURE_API(io, output); SECURE_API(io, lines); @@ -267,7 +268,6 @@ void ScriptApiSecurity::initializeSecurityClient() "getinfo", "traceback" }; - #if USE_LUAJIT static const char *jit_whitelist[] = { "arch", @@ -287,6 +287,10 @@ void ScriptApiSecurity::initializeSecurityClient() lua_State *L = getStack(); int thread = getThread(L); + // Backup globals to the registry + lua_getglobal(L, "_G"); + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); + // create an empty environment createEmptyEnv(L); @@ -303,8 +307,6 @@ void ScriptApiSecurity::initializeSecurityClient() SECURE_API(g, require); lua_pop(L, 2); - - // Copy safe OS functions lua_getglobal(L, "os"); lua_newtable(L); @@ -319,6 +321,7 @@ void ScriptApiSecurity::initializeSecurityClient() copy_safe(L, debug_whitelist, sizeof(debug_whitelist)); lua_setfield(L, -3, "debug"); lua_pop(L, 1); // Pop old debug + #if USE_LUAJIT // Copy safe jit functions, if they exist diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index 32f6a2793..3f1b89085 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -28,6 +28,8 @@ set(common_SCRIPT_LUA_API_SRCS set(client_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_camera.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_clientobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_inventoryaction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index f842671b8..fce6282a5 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -55,6 +55,11 @@ Client *ModApiBase::getClient(lua_State *L) { return getScriptApiBase(L)->getClient(); } + +Game *ModApiBase::getGame(lua_State *L) +{ + return getScriptApiBase(L)->getGame(); +} #endif IGameDef *ModApiBase::getGameDef(lua_State *L) diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index aa5905d26..9ba50dabf 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -32,6 +32,7 @@ extern "C" { #ifndef SERVER class Client; +class Game; class GUIEngine; #endif @@ -47,11 +48,11 @@ public: static ServerInventoryManager *getServerInventoryMgr(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); + static Game* getGame(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); #endif // !SERVER static IGameDef* getGameDef(lua_State *L); - static Environment* getEnv(lua_State *L); // When we are not loading the mod, this function returns "." diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index aaced7cd0..5e69d55dd 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -24,16 +24,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/clientevent.h" #include "client/sound.h" #include "client/clientenvironment.h" +#include "client/game.h" #include "common/c_content.h" #include "common/c_converter.h" #include "cpp_api/s_base.h" #include "gettext.h" #include "l_internal.h" +#include "l_clientobject.h" #include "lua_api/l_nodemeta.h" #include "gui/mainmenumanager.h" #include "map.h" #include "util/string.h" #include "nodedef.h" +#include "client/keycode.h" #define checkCSMRestrictionFlag(flag) \ ( getClient(L)->checkCSMRestrictionFlag(CSMRestrictionFlags::flag) ) @@ -414,6 +417,123 @@ int ModApiClient::l_get_csm_restrictions(lua_State *L) return 1; } +// send_damage(damage) +int ModApiClient::l_send_damage(lua_State *L) +{ + u16 damage = luaL_checknumber(L, 1); + getClient(L)->sendDamage(damage); + return 0; +} + +// place_node(pos) +int ModApiClient::l_place_node(lua_State *L) +{ + Client *client = getClient(L); + ClientMap &map = client->getEnv().getClientMap(); + LocalPlayer *player = client->getEnv().getLocalPlayer(); + ItemStack selected_item, hand_item; + player->getWieldedItem(&selected_item, &hand_item); + const ItemDefinition &selected_def = selected_item.getDefinition(getGameDef(L)->idef()); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + NodeMetadata *meta = map.getNodeMetadata(pos); + g_game->nodePlacement(selected_def, selected_item, pos, pos, pointed, meta); + return 0; +} + +// dig_node(pos) +int ModApiClient::l_dig_node(lua_State *L) +{ + Client *client = getClient(L); + v3s16 pos = read_v3s16(L, 1); + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos; + client->interact(INTERACT_START_DIGGING, pointed); + client->interact(INTERACT_DIGGING_COMPLETED, pointed); + return 0; +} + +// get_inventory(location) +int ModApiClient::l_get_inventory(lua_State *L) +{ + Client *client = getClient(L); + InventoryLocation inventory_location; + Inventory *inventory; + std::string location; + + location = readParam<std::string>(L, 1); + + try { + inventory_location.deSerialize(location); + inventory = client->getInventory(inventory_location); + if (! inventory) + throw SerializationError(std::string("Attempt to access nonexistant inventory (") + location + ")"); + push_inventory(L, inventory); + } catch (SerializationError &) { + lua_pushnil(L); + } + + return 1; +} + +// set_keypress(key_setting, pressed) -> returns true on success +int ModApiClient::l_set_keypress(lua_State *L) +{ + std::string setting_name = "keymap_" + readParam<std::string>(L, 1); + bool pressed = lua_isboolean(L, 2) && readParam<bool>(L, 2); + try { + KeyPress keyCode = getKeySetting(setting_name.c_str()); + if (pressed) + g_game->input->setKeypress(keyCode); + else + g_game->input->unsetKeypress(keyCode); + lua_pushboolean(L, true); + } catch (SettingNotFoundException &) { + lua_pushboolean(L, false); + } + return 1; +} + +// drop_selected_item() +int ModApiClient::l_drop_selected_item(lua_State *L) +{ + g_game->dropSelectedItem(); + return 0; +} + +// get_objects_inside_radius(pos, radius) +int ModApiClient::l_get_objects_inside_radius(lua_State *L) +{ + ClientEnvironment &env = getClient(L)->getEnv(); + + v3f pos = checkFloatPos(L, 1); + float radius = readParam<float>(L, 2) * BS; + + std::vector<DistanceSortedActiveObject> objs; + env.getActiveObjects(pos, radius, objs); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + ClientObjectRef::create(L, obj.obj); // TODO: getObjectRefOrCreate + lua_rawseti(L, -2, ++i); + } + return 1; +} + +//make_screenshot() +int ModApiClient::l_make_screenshot(lua_State *L) +{ + getClient(L)->makeScreenshot(); + lua_pushboolean(L, true); + return 1; +} + void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); @@ -441,4 +561,13 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(get_builtin_path); API_FCT(get_language); API_FCT(get_csm_restrictions); + API_FCT(send_damage); + API_FCT(place_node); + API_FCT(dig_node); + API_FCT(get_inventory); + API_FCT(set_keypress); + API_FCT(drop_selected_item); + API_FCT(get_objects_inside_radius); + API_FCT(make_screenshot); + } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 5dc3efdad..845f09bc9 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -105,6 +105,30 @@ private: // get_csm_restrictions() static int l_get_csm_restrictions(lua_State *L); + // send_damage(damage) + static int l_send_damage(lua_State *L); + + // place_node(pos) + static int l_place_node(lua_State *L); + + // dig_node(pos) + static int l_dig_node(lua_State *L); + + // get_inventory(location) + static int l_get_inventory(lua_State *L); + + // set_keypress(key_setting, pressed) + static int l_set_keypress(lua_State *L); + + // drop_selected_item() + static int l_drop_selected_item(lua_State *L); + + // get_objects_inside_radius(pos, radius) + static int l_get_objects_inside_radius(lua_State *L); + + // make_screenshot() + static int l_make_screenshot(lua_State *L); + public: static void Initialize(lua_State *L, int top); }; diff --git a/src/script/lua_api/l_clientobject.cpp b/src/script/lua_api/l_clientobject.cpp new file mode 100644 index 000000000..70151ba40 --- /dev/null +++ b/src/script/lua_api/l_clientobject.cpp @@ -0,0 +1,236 @@ +/* +Dragonfire +Copyright (C) 2020 system32 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_clientobject.h" +#include "l_internal.h" +#include "common/c_converter.h" +#include "client/client.h" +#include "object_properties.h" +#include "util/pointedthing.h" + +ClientObjectRef *ClientObjectRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *userdata = luaL_checkudata(L, narg, className); + if (!userdata) + luaL_typerror(L, narg, className); + return *(ClientObjectRef **)userdata; +} + +ClientActiveObject *ClientObjectRef::get_cao(ClientObjectRef *ref) +{ + ClientActiveObject *obj = ref->m_object; + return obj; +} + +GenericCAO *ClientObjectRef::get_generic_cao(ClientObjectRef *ref, lua_State *L) +{ + ClientActiveObject *obj = get_cao(ref); + ClientEnvironment &env = getClient(L)->getEnv(); + GenericCAO *gcao = env.getGenericCAO(obj->getId()); + return gcao; +} + +int ClientObjectRef::l_get_pos(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + ClientActiveObject *cao = get_cao(ref); + push_v3f(L, cao->getPosition() / BS); + return 1; +} + +int ClientObjectRef::l_get_velocity(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + push_v3f(L, gcao->getVelocity() / BS); + return 1; +} + +int ClientObjectRef::l_get_acceleration(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + push_v3f(L, gcao->getAcceleration() / BS); + return 1; +} + +int ClientObjectRef::l_get_rotation(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + push_v3f(L, gcao->getRotation()); + return 1; +} + +int ClientObjectRef::l_is_player(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + lua_pushboolean(L, gcao->isPlayer()); + return 1; +} + +int ClientObjectRef::l_is_local_player(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + lua_pushboolean(L, gcao->isLocalPlayer()); + return 1; +} + +int ClientObjectRef::l_get_name(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + lua_pushstring(L, gcao->getName().c_str()); + return 1; +} + +int ClientObjectRef::l_get_attach(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + create(L, gcao->getParent()); + return 1; +} + +int ClientObjectRef::l_get_nametag(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + ObjectProperties *props = gcao->getProperties(); + lua_pushstring(L, props->nametag.c_str()); + return 1; +} + +int ClientObjectRef::l_get_item_textures(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + ObjectProperties *props = gcao->getProperties(); + lua_newtable(L); + + for (std::string &texture : props->textures) { + lua_pushstring(L, texture.c_str()); + } + return 1; +} + +int ClientObjectRef::l_get_hp(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + lua_pushnumber(L, gcao->getHp()); + return 1; +} + +int ClientObjectRef::l_get_max_hp(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + ObjectProperties *props = gcao->getProperties(); + lua_pushnumber(L, props->hp_max); + return 1; +} + +int ClientObjectRef::l_punch(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + PointedThing pointed(gcao->getId(), v3f(0, 0, 0), v3s16(0, 0, 0), 0); + getClient(L)->interact(INTERACT_START_DIGGING, pointed); + return 0; +} + +int ClientObjectRef::l_rightclick(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + PointedThing pointed(gcao->getId(), v3f(0, 0, 0), v3s16(0, 0, 0), 0); + getClient(L)->interact(INTERACT_PLACE, pointed); + return 0; +} + +ClientObjectRef::ClientObjectRef(ClientActiveObject *object) : m_object(object) +{ +} + +void ClientObjectRef::create(lua_State *L, ClientActiveObject *object) +{ + if (object) { + ClientObjectRef *o = new ClientObjectRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + } +} + +void ClientObjectRef::create(lua_State *L, s16 id) +{ + create(L, ((ClientEnvironment *)getEnv(L))->getActiveObject(id)); +} + +int ClientObjectRef::gc_object(lua_State *L) +{ + ClientObjectRef *obj = *(ClientObjectRef **)(lua_touserdata(L, 1)); + delete obj; + return 0; +} + +// taken from LuaLocalPlayer +void ClientObjectRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // Drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // Drop methodtable +} + +const char ClientObjectRef::className[] = "ClientObjectRef"; +luaL_Reg ClientObjectRef::methods[] = {luamethod(ClientObjectRef, get_pos), + luamethod(ClientObjectRef, get_velocity), + luamethod(ClientObjectRef, get_acceleration), + luamethod(ClientObjectRef, get_rotation), + luamethod(ClientObjectRef, is_player), + luamethod(ClientObjectRef, is_local_player), + luamethod(ClientObjectRef, get_name), + luamethod(ClientObjectRef, get_attach), + luamethod(ClientObjectRef, get_nametag), + luamethod(ClientObjectRef, get_item_textures), + luamethod(ClientObjectRef, get_hp), + luamethod(ClientObjectRef, get_max_hp), luamethod(ClientObjectRef, punch), + luamethod(ClientObjectRef, rightclick), {0, 0}}; diff --git a/src/script/lua_api/l_clientobject.h b/src/script/lua_api/l_clientobject.h new file mode 100644 index 000000000..d622dc3b2 --- /dev/null +++ b/src/script/lua_api/l_clientobject.h @@ -0,0 +1,90 @@ +/* +Dragonfire +Copyright (C) 2020 system32 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_base.h" +#include "client/clientobject.h" +#include "client/content_cao.h" + +class ClientObjectRef : public ModApiBase +{ +public: + ClientObjectRef(ClientActiveObject *object); + + ~ClientObjectRef() = default; + + static void Register(lua_State *L); + + static void create(lua_State *L, ClientActiveObject *object); + static void create(lua_State *L, s16 id); + + static ClientObjectRef *checkobject(lua_State *L, int narg); + +private: + ClientActiveObject *m_object = nullptr; + static const char className[]; + static luaL_Reg methods[]; + + static ClientActiveObject *get_cao(ClientObjectRef *ref); + static GenericCAO *get_generic_cao(ClientObjectRef *ref, lua_State *L); + + static int gc_object(lua_State *L); + + // get_pos(self) + // returns: {x=num, y=num, z=num} + static int l_get_pos(lua_State *L); + + // get_velocity(self) + static int l_get_velocity(lua_State *L); + + // get_acceleration(self) + static int l_get_acceleration(lua_State *L); + + // get_rotation(self) + static int l_get_rotation(lua_State *L); + + // is_player(self) + static int l_is_player(lua_State *L); + + // is_local_player(self) + static int l_is_local_player(lua_State *L); + + // get_name(self) + static int l_get_name(lua_State *L); + + // get_attach(self) + static int l_get_attach(lua_State *L); + + // get_nametag(self) + static int l_get_nametag(lua_State *L); + + // get_textures(self) + static int l_get_item_textures(lua_State *L); + + // get_hp(self) + static int l_get_hp(lua_State *L); + + // get_max_hp(self) + static int l_get_max_hp(lua_State *L); + + // punch(self) + static int l_punch(lua_State *L); + + // rightclick(self) + static int l_rightclick(lua_State *L); +}; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 021ef2ea7..dbb0a5ab7 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -823,7 +823,6 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) int radius = luaL_checkinteger(L, 2); std::vector<content_t> filter; collectNodeIds(L, 3, ndef, filter); - int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1; #ifndef SERVER @@ -846,6 +845,165 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) return 0; } +// find_nodes_near(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector<content_t> filter; + collectNodeIds(L, 3, ndef, filter); + + int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector<u32> individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector<v3s16> &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector<content_t> filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector<u32> individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector<v3s16> &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + +// find_nodes_near_under_air_except(pos, radius, nodenames, [search_center]) +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_near_under_air_except(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector<content_t> filter; + collectNodeIds(L, 3, ndef, filter); + int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); +#endif + + std::vector<u32> individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + + for (int d = start_radius; d <= radius; d++) { + const std::vector<v3s16> &list = FacePositionCache::getFacePositions(d); + for (const v3s16 &posi : list) { + v3s16 p = pos + posi; + content_t c = map.getNode(p).getContent(); + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); + if (c == CONTENT_AIR || csurf != CONTENT_AIR) + continue; + auto it = std::find(filter.begin(), filter.end(), c); + if (it == filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } + } + } + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; +} + // find_nodes_in_area(minp, maxp, nodenames, [grouped]) int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) { @@ -1248,7 +1406,7 @@ int ModApiEnvMod::l_delete_area(lua_State *L) // max_jump, max_drop, algorithm) -> table containing path int ModApiEnvMod::l_find_path(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); v3s16 pos1 = read_v3s16(L, 1); v3s16 pos2 = read_v3s16(L, 2); @@ -1265,8 +1423,8 @@ int ModApiEnvMod::l_find_path(lua_State *L) if (algorithm == "Dijkstra") algo = PA_DIJKSTRA; } - - std::vector<v3s16> path = get_path(&env->getServerMap(), env->getGameDef()->ndef(), pos1, pos2, + + std::vector<v3s16> path = get_path(&env->getMap(), env->getGameDef()->ndef(), pos1, pos2, searchdistance, max_jump, max_drop, algo); if (!path.empty()) { @@ -1447,8 +1605,12 @@ void ModApiEnvMod::InitializeClient(lua_State *L, int top) API_FCT(get_node_level); API_FCT(find_nodes_with_meta); API_FCT(find_node_near); + API_FCT(find_nodes_near); + API_FCT(find_nodes_near_under_air); + API_FCT(find_nodes_near_under_air_except); API_FCT(find_nodes_in_area); API_FCT(find_nodes_in_area_under_air); + API_FCT(find_path); API_FCT(line_of_sight); API_FCT(raycast); } diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 7f212b5fc..ad9a0f509 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -131,6 +131,18 @@ private: // find_node_near(pos, radius, nodenames, search_center) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_node_near(lua_State *L); + + // find_nodes_near(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near(lua_State *L); + + // find_nodes_near_under_air(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near_under_air(lua_State *L); + + // find_nodes_near_under_air(pos, radius, nodenames, search_center) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_near_under_air_except(lua_State *L); // find_nodes_in_area(minp, maxp, nodenames) -> list of positions // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp index 5ea3b3f99..5a28cb369 100644 --- a/src/script/lua_api/l_http.cpp +++ b/src/script/lua_api/l_http.cpp @@ -239,18 +239,7 @@ int ModApiHttp::l_get_http_api(lua_State *L) void ModApiHttp::Initialize(lua_State *L, int top) { #if USE_CURL - - bool isMainmenu = false; -#ifndef SERVER - isMainmenu = ModApiBase::getGuiEngine(L) != nullptr; -#endif - - if (isMainmenu) { - API_FCT(get_http_api); - } else { - API_FCT(request_http_api); - } - + API_FCT(get_http_api); #endif } diff --git a/src/script/lua_api/l_inventoryaction.cpp b/src/script/lua_api/l_inventoryaction.cpp new file mode 100644 index 000000000..f65137465 --- /dev/null +++ b/src/script/lua_api/l_inventoryaction.cpp @@ -0,0 +1,215 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_inventoryaction.h" +#include "l_internal.h" +#include "client/client.h" + +int LuaInventoryAction::gc_object(lua_State *L) +{ + LuaInventoryAction *o = *(LuaInventoryAction **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +int LuaInventoryAction::mt_tostring(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + std::ostringstream os(std::ios::binary); + o->m_action->serialize(os); + lua_pushfstring(L, "InventoryAction(\"%s\")", os.str().c_str()); + return 1; +} + +int LuaInventoryAction::l_apply(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + std::ostringstream os(std::ios::binary); + o->m_action->serialize(os); + + std::istringstream is(os.str(), std::ios_base::binary); + + InventoryAction *a = InventoryAction::deSerialize(is); + + getClient(L)->inventoryAction(a); + return 0; +} + +int LuaInventoryAction::l_from(lua_State *L) +{ + GET_MOVE_ACTION + readFullInventoryLocationInto(L, &act->from_inv, &act->from_list, &act->from_i); + return 0; +} + +int LuaInventoryAction::l_to(lua_State *L) +{ + GET_MOVE_ACTION + readFullInventoryLocationInto(L, &act->to_inv, &act->to_list, &act->to_i); + return 0; +} + +int LuaInventoryAction::l_craft(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + if (o->m_action->getType() != IAction::Craft) + return 0; + + std::string locStr; + InventoryLocation loc; + + locStr = readParam<std::string>(L, 2); + + try { + loc.deSerialize(locStr); + dynamic_cast<ICraftAction *>(o->m_action)->craft_inv = loc; + } catch (SerializationError &) { + } + + return 0; +} + +int LuaInventoryAction::l_set_count(lua_State *L) +{ + LuaInventoryAction *o = checkobject(L, 1); + + s16 count = luaL_checkinteger(L, 2); + + switch (o->m_action->getType()) { + case IAction::Move: + ((IMoveAction *)o->m_action)->count = count; + break; + case IAction::Drop: + ((IDropAction *)o->m_action)->count = count; + break; + case IAction::Craft: + ((ICraftAction *)o->m_action)->count = count; + break; + } + + return 0; +} + +LuaInventoryAction::LuaInventoryAction(const IAction &type) : m_action(nullptr) +{ + switch (type) { + case IAction::Move: + m_action = new IMoveAction(); + break; + case IAction::Drop: + m_action = new IDropAction(); + break; + case IAction::Craft: + m_action = new ICraftAction(); + break; + } +} + +LuaInventoryAction::~LuaInventoryAction() +{ + delete m_action; +} + +void LuaInventoryAction::readFullInventoryLocationInto( + lua_State *L, InventoryLocation *loc, std::string *list, s16 *index) +{ + try { + loc->deSerialize(readParam<std::string>(L, 2)); + std::string l = readParam<std::string>(L, 3); + *list = l; + *index = luaL_checkinteger(L, 4) - 1; + } catch (SerializationError &) { + } +} + +int LuaInventoryAction::create_object(lua_State *L) +{ + IAction type; + std::string typeStr; + + typeStr = readParam<std::string>(L, 1); + + if (typeStr == "move") + type = IAction::Move; + else if (typeStr == "drop") + type = IAction::Drop; + else if (typeStr == "craft") + type = IAction::Craft; + else + return 0; + + LuaInventoryAction *o = new LuaInventoryAction(type); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +int LuaInventoryAction::create(lua_State *L, const IAction &type) +{ + LuaInventoryAction *o = new LuaInventoryAction(type); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaInventoryAction *LuaInventoryAction::checkobject(lua_State *L, int narg) +{ + return *(LuaInventoryAction **)luaL_checkudata(L, narg, className); +} + +void LuaInventoryAction::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, mt_tostring); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + +const char LuaInventoryAction::className[] = "InventoryAction"; +const luaL_Reg LuaInventoryAction::methods[] = {luamethod(LuaInventoryAction, apply), + luamethod(LuaInventoryAction, from), luamethod(LuaInventoryAction, to), + luamethod(LuaInventoryAction, craft), + luamethod(LuaInventoryAction, set_count), {0, 0}}; diff --git a/src/script/lua_api/l_inventoryaction.h b/src/script/lua_api/l_inventoryaction.h new file mode 100644 index 000000000..a4cc6cbe5 --- /dev/null +++ b/src/script/lua_api/l_inventoryaction.h @@ -0,0 +1,74 @@ +/* +Dragonfire +Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "inventorymanager.h" +#include "lua_api/l_base.h" + +#define GET_MOVE_ACTION \ + LuaInventoryAction *o = checkobject(L, 1); \ + if (o->m_action->getType() == IAction::Craft) \ + return 0; \ + MoveAction *act = dynamic_cast<MoveAction *>(o->m_action); + +class LuaInventoryAction : public ModApiBase +{ +private: + InventoryAction *m_action; + + static void readFullInventoryLocationInto(lua_State *L, InventoryLocation *loc, + std::string *list, s16 *index); + + static const char className[]; + static const luaL_Reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // __tostring metamethod + static int mt_tostring(lua_State *L); + + // apply(self) + static int l_apply(lua_State *L); + + // from(self, location, list, index) + static int l_from(lua_State *L); + + // to(self, location, list, index) + static int l_to(lua_State *L); + + // craft(self, location) + static int l_craft(lua_State *L); + + // set_count(self, count) + static int l_set_count(lua_State *L); + +public: + LuaInventoryAction(const IAction &type); + ~LuaInventoryAction(); + + // LuaInventoryAction(inventory action type) + // Creates an LuaInventoryAction and leaves it on top of stack + static int create_object(lua_State *L); + // Not callable from Lua + static int create(lua_State *L, const IAction &type); + static LuaInventoryAction *checkobject(lua_State *L, int narg); + static void Register(lua_State *L); +}; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 9e0da4034..b098eccf0 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -27,7 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "inventory.h" #include "log.h" - +#include "script/cpp_api/s_base.h" +#ifndef SERVER +#include "client/client.h" +#include "client/renderingengine.h" +#include "client/shader.h" +#endif // garbage collector int LuaItemStack::gc_object(lua_State *L) @@ -534,9 +539,9 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) // Get the writable item and node definition managers from the server IWritableItemDefManager *idef = - getServer(L)->getWritableItemDefManager(); + getGameDef(L)->getWritableItemDefManager(); NodeDefManager *ndef = - getServer(L)->getWritableNodeDefManager(); + getGameDef(L)->getWritableNodeDefManager(); // Check if name is defined std::string name; @@ -584,8 +589,9 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) + itos(MAX_REGISTERED_CONTENT+1) + ") exceeded (" + name + ")"); } + } - + return 0; /* number of results */ } @@ -596,12 +602,12 @@ int ModApiItemMod::l_unregister_item_raw(lua_State *L) std::string name = luaL_checkstring(L, 1); IWritableItemDefManager *idef = - getServer(L)->getWritableItemDefManager(); + getGameDef(L)->getWritableItemDefManager(); // Unregister the node if (idef->get(name).type == ITEM_NODE) { NodeDefManager *ndef = - getServer(L)->getWritableNodeDefManager(); + getGameDef(L)->getWritableNodeDefManager(); ndef->removeNode(name); } @@ -619,7 +625,7 @@ int ModApiItemMod::l_register_alias_raw(lua_State *L) // Get the writable item definition manager from the server IWritableItemDefManager *idef = - getServer(L)->getWritableItemDefManager(); + getGameDef(L)->getWritableItemDefManager(); idef->registerAlias(name, convert_to); diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 33fa27c8b..3f4147227 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "l_clientobject.h" #include "l_localplayer.h" #include "l_internal.h" #include "lua_api/l_item.h" @@ -24,7 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/localplayer.h" #include "hud.h" #include "common/c_content.h" +#include "client/client.h" #include "client/content_cao.h" +#include "client/game.h" LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m) : m_localplayer(m) { @@ -60,6 +63,57 @@ int LuaLocalPlayer::l_get_velocity(lua_State *L) return 1; } +int LuaLocalPlayer::l_set_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + v3f pos = checkFloatPos(L, 2); + player->setSpeed(pos); + + return 0; +} + +int LuaLocalPlayer::l_get_yaw(lua_State *L) +{ + lua_pushnumber(L, wrapDegrees_0_360(g_game->cam_view.camera_yaw)); + return 1; +} + +int LuaLocalPlayer::l_set_yaw(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + if (lua_isnumber(L, 2)) { + int yaw = lua_tonumber(L, 2); + player->setYaw(yaw); + g_game->cam_view.camera_yaw = yaw; + g_game->cam_view_target.camera_yaw = yaw; + } + + return 0; +} + +int LuaLocalPlayer::l_get_pitch(lua_State *L) +{ + lua_pushnumber(L, -wrapDegrees_180(g_game->cam_view.camera_pitch) ); + return 1; +} + +int LuaLocalPlayer::l_set_pitch(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + if (lua_isnumber(L, 2)) { + int pitch = lua_tonumber(L, 2); + player->setPitch(pitch); + g_game->cam_view.camera_pitch = pitch; + g_game->cam_view_target.camera_pitch = pitch; + } + + return 0; +} + + int LuaLocalPlayer::l_get_hp(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -81,10 +135,24 @@ int LuaLocalPlayer::l_get_wield_index(lua_State *L) { LocalPlayer *player = getobject(L, 1); - lua_pushinteger(L, player->getWieldIndex()); + lua_pushinteger(L, player->getWieldIndex() + 1); return 1; } +// set_wield_index(self) +int LuaLocalPlayer::l_set_wield_index(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + u32 index = luaL_checkinteger(L, 2) - 1; + + player->setWieldIndex(index); + g_game->processItemSelection(&g_game->runData.new_playeritem); + ItemStack selected_item, hand_item; + ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); + g_game->camera->wield(tool_item); + return 0; +} + // get_wielded_item(self) int LuaLocalPlayer::l_get_wielded_item(lua_State *L) { @@ -157,26 +225,30 @@ int LuaLocalPlayer::l_get_physics_override(lua_State *L) { LocalPlayer *player = getobject(L, 1); - lua_newtable(L); - lua_pushnumber(L, player->physics_override_speed); - lua_setfield(L, -2, "speed"); - - lua_pushnumber(L, player->physics_override_jump); - lua_setfield(L, -2, "jump"); - - lua_pushnumber(L, player->physics_override_gravity); - lua_setfield(L, -2, "gravity"); - - lua_pushboolean(L, player->physics_override_sneak); - lua_setfield(L, -2, "sneak"); - - lua_pushboolean(L, player->physics_override_sneak_glitch); - lua_setfield(L, -2, "sneak_glitch"); + push_physics_override(L, player->physics_override_speed, player->physics_override_jump, player->physics_override_gravity, player->physics_override_sneak, player->physics_override_sneak_glitch, player->physics_override_new_move); + + return 1; +} - lua_pushboolean(L, player->physics_override_new_move); - lua_setfield(L, -2, "new_move"); +// set_physics_override(self, override) +int LuaLocalPlayer::l_set_physics_override(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + player->physics_override_speed = getfloatfield_default( + L, 2, "speed", player->physics_override_speed); + player->physics_override_jump = getfloatfield_default( + L, 2, "jump", player->physics_override_jump); + player->physics_override_gravity = getfloatfield_default( + L, 2, "gravity", player->physics_override_gravity); + player->physics_override_sneak = getboolfield_default( + L, 2, "sneak", player->physics_override_sneak); + player->physics_override_sneak_glitch = getboolfield_default( + L, 2, "sneak_glitch", player->physics_override_sneak_glitch); + player->physics_override_new_move = getboolfield_default( + L, 2, "new_move", player->physics_override_new_move); - return 1; + return 0; } int LuaLocalPlayer::l_get_last_pos(lua_State *L) @@ -255,6 +327,17 @@ int LuaLocalPlayer::l_get_pos(lua_State *L) return 1; } +// set_pos(self, pos) +int LuaLocalPlayer::l_set_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + v3f pos = checkFloatPos(L, 2); + player->setPosition(pos); + getClient(L)->sendPlayerPos(); + return 0; +} + // get_movement_acceleration(self) int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L) { @@ -393,6 +476,17 @@ int LuaLocalPlayer::l_hud_get(lua_State *L) return 1; } +int LuaLocalPlayer::l_get_object(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + ClientEnvironment &env = getClient(L)->getEnv(); + ClientActiveObject *obj = env.getGenericCAO(player->getCAO()->getId()); + + ClientObjectRef::create(L, obj); + + return 1; +} + LuaLocalPlayer *LuaLocalPlayer::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); @@ -453,9 +547,15 @@ void LuaLocalPlayer::Register(lua_State *L) const char LuaLocalPlayer::className[] = "LocalPlayer"; const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, get_velocity), + luamethod(LuaLocalPlayer, set_velocity), + luamethod(LuaLocalPlayer, get_yaw), + luamethod(LuaLocalPlayer, set_yaw), + luamethod(LuaLocalPlayer, get_pitch), + luamethod(LuaLocalPlayer, set_pitch), luamethod(LuaLocalPlayer, get_hp), luamethod(LuaLocalPlayer, get_name), luamethod(LuaLocalPlayer, get_wield_index), + luamethod(LuaLocalPlayer, set_wield_index), luamethod(LuaLocalPlayer, get_wielded_item), luamethod(LuaLocalPlayer, is_attached), luamethod(LuaLocalPlayer, is_touching_ground), @@ -465,6 +565,7 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, is_climbing), luamethod(LuaLocalPlayer, swimming_vertical), luamethod(LuaLocalPlayer, get_physics_override), + luamethod(LuaLocalPlayer, set_physics_override), // TODO: figure our if these are useful in any way luamethod(LuaLocalPlayer, get_last_pos), luamethod(LuaLocalPlayer, get_last_velocity), @@ -474,6 +575,7 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, get_control), luamethod(LuaLocalPlayer, get_breath), luamethod(LuaLocalPlayer, get_pos), + luamethod(LuaLocalPlayer, set_pos), luamethod(LuaLocalPlayer, get_movement_acceleration), luamethod(LuaLocalPlayer, get_movement_speed), luamethod(LuaLocalPlayer, get_movement), @@ -482,6 +584,7 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, hud_remove), luamethod(LuaLocalPlayer, hud_change), luamethod(LuaLocalPlayer, hud_get), + luamethod(LuaLocalPlayer, get_object), {0, 0} }; diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h index 4413f2bdb..33e23d178 100644 --- a/src/script/lua_api/l_localplayer.h +++ b/src/script/lua_api/l_localplayer.h @@ -35,6 +35,21 @@ private: // get_velocity(self) static int l_get_velocity(lua_State *L); + // set_velocity(self, vel) + static int l_set_velocity(lua_State *L); + + // get_yaw(self) + static int l_get_yaw(lua_State *L); + + // set_yaw(self, yaw) + static int l_set_yaw(lua_State *L); + + // get_pitch(self) + static int l_get_pitch(lua_State *L); + + // set_pitch(self,pitch) + static int l_set_pitch(lua_State *L); + // get_hp(self) static int l_get_hp(lua_State *L); @@ -44,6 +59,9 @@ private: // get_wield_index(self) static int l_get_wield_index(lua_State *L); + // set_wield_index(self) + static int l_set_wield_index(lua_State *L); + // get_wielded_item(self) static int l_get_wielded_item(lua_State *L); @@ -56,6 +74,7 @@ private: static int l_swimming_vertical(lua_State *L); static int l_get_physics_override(lua_State *L); + static int l_set_physics_override(lua_State *L); static int l_get_override_pos(lua_State *L); @@ -73,6 +92,9 @@ private: // get_pos(self) static int l_get_pos(lua_State *L); + // set_pos(self, pos) + static int l_set_pos(lua_State *L); + // get_movement_acceleration(self) static int l_get_movement_acceleration(lua_State *L); @@ -96,6 +118,9 @@ private: // hud_get(self, id) static int l_hud_get(lua_State *L); + // get_object(self) + static int l_get_object(lua_State *L); + LocalPlayer *m_localplayer = nullptr; public: diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 6f934bb9d..64ae924d2 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -338,6 +338,7 @@ int ModApiServer::l_kick_player(lua_State *L) return 1; } +// remove_player(name) int ModApiServer::l_remove_player(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -562,7 +563,6 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(get_ban_description); API_FCT(ban_player); API_FCT(kick_player); - API_FCT(remove_player); API_FCT(unban_player_or_ip); API_FCT(notify_authentication_modified); diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index cd63e20c2..4595dc1c1 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -527,15 +527,20 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(is_yes); API_FCT(is_nan); - + API_FCT(compress); API_FCT(decompress); + API_FCT(request_insecure_environment); + API_FCT(encode_base64); API_FCT(decode_base64); API_FCT(get_version); API_FCT(sha1); + + LuaSettings::create(L, g_settings, g_settings_path); + lua_setfield(L, top, "settings"); } void ModApiUtil::InitializeAsync(lua_State *L, int top) diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp index 6643a9509..729645678 100644 --- a/src/script/scripting_client.cpp +++ b/src/script/scripting_client.cpp @@ -20,9 +20,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_client.h" #include "client/client.h" +#include "client/game.h" #include "cpp_api/s_internal.h" #include "lua_api/l_client.h" +#include "lua_api/l_clientobject.h" #include "lua_api/l_env.h" +#include "lua_api/l_inventoryaction.h" #include "lua_api/l_item.h" #include "lua_api/l_itemstackmeta.h" #include "lua_api/l_minimap.h" @@ -35,11 +38,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodemeta.h" #include "lua_api/l_localplayer.h" #include "lua_api/l_camera.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" ClientScripting::ClientScripting(Client *client): ScriptApiBase(ScriptingType::Client) { setGameDef(client); + setGame(g_game); SCRIPTAPI_PRECHECKHEADER @@ -73,8 +79,13 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) LuaLocalPlayer::Register(L); LuaCamera::Register(L); ModChannelRef::Register(L); + LuaSettings::Register(L); + ClientObjectRef::Register(L); + LuaInventoryAction::Register(L); + ModApiItemMod::Initialize(L, top); ModApiUtil::InitializeClient(L, top); + ModApiHttp::Initialize(L, top); ModApiClient::Initialize(L, top); ModApiStorage::Initialize(L, top); ModApiEnvMod::InitializeClient(L, top); diff --git a/src/script/scripting_client.h b/src/script/scripting_client.h index 3088029f0..e162f8bcf 100644 --- a/src/script/scripting_client.h +++ b/src/script/scripting_client.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_base.h" #include "cpp_api/s_client.h" +#include "cpp_api/s_cheats.h" #include "cpp_api/s_modchannels.h" #include "cpp_api/s_security.h" @@ -34,7 +35,8 @@ class ClientScripting: virtual public ScriptApiBase, public ScriptApiSecurity, public ScriptApiClient, - public ScriptApiModChannels + public ScriptApiModChannels, + public ScriptApiCheats { public: ClientScripting(Client *client); diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index a783ccd32..0f6b36649 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -47,7 +47,9 @@ public: ~TestGameDef(); IItemDefManager *getItemDefManager() { return m_itemdef; } + IWritableItemDefManager *getWritableItemDefManager() { return m_itemdef; } const NodeDefManager *getNodeDefManager() { return m_nodedef; } + NodeDefManager *getWritableNodeDefManager() { return m_nodedef; } ICraftDefManager *getCraftDefManager() { return m_craftdef; } ITextureSource *getTextureSource() { return m_texturesrc; } IShaderSource *getShaderSource() { return m_shadersrc; } @@ -80,8 +82,8 @@ public: } private: - IItemDefManager *m_itemdef = nullptr; - const NodeDefManager *m_nodedef = nullptr; + IWritableItemDefManager *m_itemdef = nullptr; + NodeDefManager *m_nodedef = nullptr; ICraftDefManager *m_craftdef = nullptr; ITextureSource *m_texturesrc = nullptr; IShaderSource *m_shadersrc = nullptr; |