diff options
Diffstat (limited to 'src')
80 files changed, 4310 insertions, 1199 deletions
diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index aa0538e60..835f19dc2 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -43,6 +43,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 7cc9cb6e8..164db8761 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "constants.h" #include "fontengine.h" +#include "guiscalingfilter.h" #include "script/scripting_client.h" #include "gettext.h" @@ -322,7 +323,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) // 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 after the player instantly moves upward due to stepheight. // The smoothing usually continues until the camera position reaches the player position. @@ -679,7 +680,7 @@ void Camera::drawNametags() screen_pos.Y = screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5) - textsize.Height / 2; core::rect<s32> size(0, 0, textsize.Width, textsize.Height); - core::rect<s32> bg_size(-2, 0, textsize.Width+2, textsize.Height); + core::rect<s32> bg_size(-2, 0, std::max(textsize.Width+2, (u32) nametag->images_dim.Width), textsize.Height + nametag->images_dim.Height); auto bgcolor = nametag->getBgColor(m_show_nametag_backgrounds); if (bgcolor.getAlpha() != 0) @@ -688,15 +689,29 @@ void Camera::drawNametags() font->draw( translate_string(utf8_to_wide(nametag->text)).c_str(), size + screen_pos, nametag->textcolor); + + v2s32 image_pos(screen_pos); + image_pos.Y += textsize.Height; + + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + + for (video::ITexture *texture : nametag->images) { + core::dimension2di imgsize(texture->getOriginalSize()); + core::rect<s32> rect(core::position2d<s32>(0, 0), imgsize); + draw2DImageFilterScaled(driver, texture, rect + image_pos, rect, NULL, colors, true); + image_pos += core::dimension2di(imgsize.Width, 0); + } + } } } - Nametag *Camera::addNametag(scene::ISceneNode *parent_node, const std::string &text, video::SColor textcolor, - Optional<video::SColor> bgcolor, const v3f &pos) + Optional<video::SColor> bgcolor, const v3f &pos, + const std::vector<std::string> &images) { - Nametag *nametag = new Nametag(parent_node, text, textcolor, bgcolor, pos); + Nametag *nametag = new Nametag(parent_node, text, textcolor, bgcolor, pos, m_client->tsrc(), images); m_nametags.push_back(nametag); return nametag; } diff --git a/src/client/camera.h b/src/client/camera.h index cbf248d97..6a8cf650d 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -40,18 +40,44 @@ struct Nametag video::SColor textcolor; Optional<video::SColor> bgcolor; v3f pos; + ITextureSource *texture_source; + std::vector<video::ITexture *> images; + core::dimension2di images_dim; Nametag(scene::ISceneNode *a_parent_node, const std::string &text, const video::SColor &textcolor, const Optional<video::SColor> &bgcolor, - const v3f &pos): + const v3f &pos, + ITextureSource *tsrc, + const std::vector<std::string> &image_names): parent_node(a_parent_node), text(text), textcolor(textcolor), bgcolor(bgcolor), - pos(pos) + pos(pos), + texture_source(tsrc), + images(), + images_dim(0, 0) { + setImages(image_names); + } + + void setImages(const std::vector<std::string> &image_names) + { + images.clear(); + images_dim = core::dimension2di(0, 0); + + for (const std::string &image_name : image_names) { + video::ITexture *texture = texture_source->getTexture(image_name); + core::dimension2di imgsize(texture->getOriginalSize()); + + images_dim.Width += imgsize.Width; + if (images_dim.Height < imgsize.Height) + images_dim.Height = imgsize.Height; + + images.push_back(texture); + } } video::SColor getBgColor(bool use_fallback) const @@ -181,7 +207,8 @@ public: Nametag *addNametag(scene::ISceneNode *parent_node, const std::string &text, video::SColor textcolor, - Optional<video::SColor> bgcolor, const v3f &pos); + Optional<video::SColor> bgcolor, const v3f &pos, + const std::vector<std::string> &image_names); void removeNametag(Nametag *nametag); diff --git a/src/client/client.cpp b/src/client/client.cpp index d81ee434e..7337229fb 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" @@ -103,6 +104,7 @@ Client::Client( GameUI *game_ui, ELoginRegister allow_login_or_register ): + m_mesh_update_thread(this), m_tsrc(tsrc), m_shsrc(shsrc), m_itemdef(itemdef), @@ -110,7 +112,6 @@ Client::Client( m_sound(sound), m_event(event), m_rendering_engine(rendering_engine), - m_mesh_update_thread(this), m_env( new ClientMap(this, rendering_engine, control, 666), tsrc, this @@ -226,6 +227,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()); @@ -486,7 +489,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 @@ -964,8 +967,8 @@ 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->getSendSpeed() * 100; s32 pitch = myplayer->getPitch() * 100; s32 yaw = myplayer->getYaw() * 100; u32 keyPressed = myplayer->control.getKeysPressed(); @@ -1306,7 +1309,7 @@ void Client::sendReady() Send(&pkt); } -void Client::sendPlayerPos() +void Client::sendPlayerPos(v3f pos) { LocalPlayer *player = m_env.getLocalPlayer(); if (!player) @@ -1325,8 +1328,8 @@ void Client::sendPlayerPos() u32 keyPressed = player->control.getKeysPressed(); if ( - player->last_position == player->getPosition() && - player->last_speed == player->getSpeed() && + player->last_position == pos && + player->last_speed == player->getSendSpeed() && player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == keyPressed && @@ -1334,8 +1337,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->getSendSpeed(); player->last_pitch = player->getPitch(); player->last_yaw = player->getYaw(); player->last_keyPressed = keyPressed; @@ -1349,6 +1352,14 @@ void Client::sendPlayerPos() Send(&pkt); } +void Client::sendPlayerPos() +{ + LocalPlayer *player = m_env.getLocalPlayer(); + if (!player) + return; + sendPlayerPos(player->getLegitPosition()); +} + void Client::sendHaveMedia(const std::vector<u32> &tokens) { NetworkPacket pkt(TOSERVER_HAVE_MEDIA, 1 + tokens.size() * 4); @@ -1486,6 +1497,7 @@ Inventory* Client::getInventory(const InventoryLocation &loc) case InventoryLocation::UNDEFINED: {} break; + case InventoryLocation::PLAYER: case InventoryLocation::CURRENT_PLAYER: { LocalPlayer *player = m_env.getLocalPlayer(); @@ -1493,15 +1505,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); @@ -1665,6 +1668,25 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur addUpdateMeshTask(blockpos + v3s16(0, 0, -1), false, urgent); } +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(), @@ -1890,10 +1912,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 f01510ddb..c1b8cfbaa 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -49,7 +49,6 @@ class MapBlockMesh; class RenderingEngine; class IWritableTextureSource; class IWritableShaderSource; -class IWritableItemDefManager; class ISoundManager; class NodeDefManager; //class IWritableCraftDefManager; @@ -297,7 +296,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; } @@ -312,6 +311,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; } @@ -321,7 +322,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) { @@ -370,7 +371,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(); @@ -378,8 +381,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); ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; } @@ -428,7 +430,8 @@ public: inline bool checkCSMRestrictionFlag(CSMRestrictionFlags flag) const { - return m_csm_restriction_flags & flag; + //return m_csm_restriction_flags & flag; + return false; } bool joinModChannel(const std::string &channel) override; @@ -441,6 +444,11 @@ public: { return m_env.getLocalPlayer()->formspec_prepend; } + + void sendPlayerPos(v3f pos); + void sendPlayerPos(); + MeshUpdateThread m_mesh_update_thread; + private: void loadMods(); @@ -454,7 +462,6 @@ private: void ReceiveAll(); - void sendPlayerPos(); void deleteAuthData(); // helper method shared with clientpackethandler @@ -487,7 +494,6 @@ private: RenderingEngine *m_rendering_engine; - 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 183a95007..e847161f9 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -143,8 +143,8 @@ void ClientEnvironment::step(float dtime) stepTimeOfDay(dtime); // Get some settings - bool fly_allowed = m_client->checkLocalPrivilege("fly"); - bool free_move = fly_allowed && g_settings->getBool("free_move"); + bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam"); + bool free_move = (fly_allowed && g_settings->getBool("free_move")) || g_settings->getBool("freecam"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); @@ -193,8 +193,7 @@ void ClientEnvironment::step(float dtime) // Control local player lplayer->applyControl(dtime_part, this); - // Apply physics - if (!free_move) { + if (!free_move && !g_settings->getBool("freecam")) { // Gravity v3f speed = lplayer->getSpeed(); if (!is_climbing && !lplayer->in_liquid) @@ -374,6 +373,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, { ClientActiveObject* obj = ClientActiveObject::create((ActiveObjectType) type, m_client, this); + if(obj == NULL) { infostream<<"ClientEnvironment::addActiveObject(): " @@ -384,6 +384,9 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, obj->setId(id); + if (m_client->modsLoaded()) + m_client->getScript()->addObjectReference(dynamic_cast<ActiveObject*>(obj)); + try { obj->initialize(init_data); @@ -416,9 +419,14 @@ void ClientEnvironment::removeActiveObject(u16 id) { // Get current attachment childs to detach them visually std::unordered_set<int> attachment_childs; - if (auto *obj = getActiveObject(id)) + auto *obj = getActiveObject(id); + if (obj) { attachment_childs = obj->getAttachmentChildIds(); + if (m_client->modsLoaded()) + m_client->getScript()->removeObjectReference(dynamic_cast<ActiveObject*>(obj)); + } + m_ao_manager.removeObject(id); // Perform a proper detach in Irrlicht 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 38ba1daad..3a89b8803 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -221,7 +221,7 @@ void ClientMap::updateDrawList() // No occlusion culling when free_move is on and camera is inside ground bool occlusion_culling_enabled = true; - if (m_control.allow_noclip) { + if (m_control.allow_noclip || g_settings->getBool("freecam")) { MapNode n = getNode(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2) occlusion_culling_enabled = false; @@ -682,7 +682,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) // If the camera is in a solid node, make everything black. // (first person mode only) if (features.solidness == 2 && cam_mode == CAMERA_MODE_FIRST && - !m_control.allow_noclip) { + !(m_control.allow_noclip || g_settings->getBool("freecam"))) { post_effect_color = video::SColor(255, 0, 0, 0); } diff --git a/src/client/clientobject.h b/src/client/clientobject.h index b192f0dcd..c0b14497c 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 9c3e5aa05..eeeb19ac5 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -46,6 +46,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; @@ -253,7 +254,7 @@ void TestCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) 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); @@ -489,11 +490,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 @@ -502,7 +506,7 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, } else if (!m_is_local_player) { // Objects attached to the local player should be hidden in first person m_is_visible = !m_attached_to_local || - m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST; + m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST || g_settings->getBool("freecam"); m_force_visible = false; } else { // Local players need to have this set, @@ -854,6 +858,14 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) } } } + + if (m_client->modsLoaded() && m_client->getScript()->on_object_add(m_id)) { + removeFromScene(false); + return; + } + + if (m_client->modsLoaded()) + m_client->getScript()->on_object_properties_change(m_id); } void GenericCAO::updateLight(u32 day_night_ratio) @@ -887,6 +899,9 @@ void GenericCAO::updateLight(u32 day_night_ratio) if (!m_enable_shaders) final_color_blend(&light, light_at_pos, day_night_ratio); + if (g_settings->getBool("fullbright")) + light = video::SColor(0xFFFFFFFF); + if (light != m_last_light) { m_last_light = light; setNodeLight(light); @@ -963,8 +978,8 @@ void GenericCAO::updateMarker() void GenericCAO::updateNametag() { - if (m_is_local_player) // No nametag for local player - return; + //if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player + //return; if (m_prop.nametag.empty() || m_prop.nametag_color.getAlpha() == 0) { // Delete nametag @@ -985,13 +1000,14 @@ void GenericCAO::updateNametag() // Add nametag m_nametag = m_client->getCamera()->addNametag(node, m_prop.nametag, m_prop.nametag_color, - m_prop.nametag_bgcolor, pos); + m_prop.nametag_bgcolor, pos, nametag_images); } else { // Update nametag m_nametag->text = m_prop.nametag; m_nametag->textcolor = m_prop.nametag_color; m_nametag->bgcolor = m_prop.nametag_bgcolor; m_nametag->pos = pos; + m_nametag->setImages(nametag_images); } } @@ -1019,10 +1035,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; @@ -1033,7 +1051,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) f32 new_speed = player->local_animation_speed; bool walking = false; - if (controls.movement_speed > 0.001f) { + if (controls.movement_speed > 0.001f && ! g_settings->getBool("freecam")) { new_speed *= controls.movement_speed; walking = true; } @@ -1042,15 +1060,15 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) bool allow_update = false; // increase speed if using fast or flying fast - if((g_settings->getBool("fast_move") && + if(((g_settings->getBool("fast_move") && m_client->checkLocalPrivilege("fast")) && (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_client->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) || g_settings->getBool("freecam")) new_speed *= 1.5; // slowdown speed if sneaking - if (controls.sneak && walking) + if (controls.sneak && walking && ! g_settings->getBool("no_slow")) new_speed /= 2; if (walking && (controls.dig || controls.place)) { @@ -1661,6 +1679,57 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const (uses_legacy_texture && old.textures != new_.textures); } +void GenericCAO::setProperties(ObjectProperties newprops) +{ + // Check what exactly changed + bool expire_visuals = visualExpiryRequired(newprops); + bool textures_changed = m_prop.textures != newprops.textures; + + // Apply changes + m_prop = std::move(newprops); + + m_selection_box = m_prop.selectionbox; + m_selection_box.MinEdge *= BS; + m_selection_box.MaxEdge *= BS; + + m_tx_size.X = 1.0f / m_prop.spritediv.X; + m_tx_size.Y = 1.0f / m_prop.spritediv.Y; + + if(!m_initial_tx_basepos_set){ + m_initial_tx_basepos_set = true; + m_tx_basepos = m_prop.initial_sprite_basepos; + } + if (m_is_local_player) { + LocalPlayer *player = m_env->getLocalPlayer(); + player->makes_footstep_sound = m_prop.makes_footstep_sound; + aabb3f collision_box = m_prop.collisionbox; + collision_box.MinEdge *= BS; + collision_box.MaxEdge *= BS; + player->setCollisionbox(collision_box); + player->setEyeHeight(m_prop.eye_height); + player->setZoomFOV(m_prop.zoom_fov); + } + + if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) + m_prop.nametag = m_name; + if (m_is_local_player) + m_prop.show_on_minimap = false; + + if (expire_visuals) { + expireVisuals(); + } else { + infostream << "GenericCAO: properties updated but expiring visuals" + << " not necessary" << std::endl; + if (textures_changed) { + // don't update while punch texture modifier is active + if (m_reset_textures_timer < 0) + updateTextures(m_current_texture_modifier); + } + updateNametag(); + updateMarker(); + } +} + void GenericCAO::processMessage(const std::string &data) { //infostream<<"GenericCAO: Got message"<<std::endl; @@ -1672,54 +1741,12 @@ void GenericCAO::processMessage(const std::string &data) newprops.show_on_minimap = m_is_player; // default newprops.deSerialize(is); + setProperties(newprops); - // Check what exactly changed - bool expire_visuals = visualExpiryRequired(newprops); - bool textures_changed = m_prop.textures != newprops.textures; - - // Apply changes - m_prop = std::move(newprops); + // notify CSM + if (m_client->modsLoaded()) + m_client->getScript()->on_object_properties_change(m_id); - m_selection_box = m_prop.selectionbox; - m_selection_box.MinEdge *= BS; - m_selection_box.MaxEdge *= BS; - - m_tx_size.X = 1.0f / m_prop.spritediv.X; - m_tx_size.Y = 1.0f / m_prop.spritediv.Y; - - if(!m_initial_tx_basepos_set){ - m_initial_tx_basepos_set = true; - m_tx_basepos = m_prop.initial_sprite_basepos; - } - if (m_is_local_player) { - LocalPlayer *player = m_env->getLocalPlayer(); - player->makes_footstep_sound = m_prop.makes_footstep_sound; - aabb3f collision_box = m_prop.collisionbox; - collision_box.MinEdge *= BS; - collision_box.MaxEdge *= BS; - player->setCollisionbox(collision_box); - player->setEyeHeight(m_prop.eye_height); - player->setZoomFOV(m_prop.zoom_fov); - } - - if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) - m_prop.nametag = m_name; - if (m_is_local_player) - m_prop.show_on_minimap = false; - - if (expire_visuals) { - expireVisuals(); - } else { - infostream << "GenericCAO: properties updated but expiring visuals" - << " not necessary" << std::endl; - if (textures_changed) { - // don't update while punch texture modifier is active - if (m_reset_textures_timer < 0) - updateTextures(m_current_texture_modifier); - } - updateNametag(); - updateMarker(); - } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. // We might however get here if the server notices the object being detached before the client. @@ -1784,6 +1811,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; @@ -1882,6 +1914,9 @@ void GenericCAO::processMessage(const std::string &data) // Same as 'ObjectRef::l_remove' if (!m_is_player) clearChildAttachments(); + } else { + if (m_client->modsLoaded()) + m_client->getScript()->on_object_hp_change(m_id); } } else if (cmd == AO_CMD_UPDATE_ARMOR_GROUPS) { m_armor_groups.clear(); @@ -1968,7 +2003,7 @@ void GenericCAO::updateMeshCulling() if (!m_is_local_player) return; - const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST; + const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST && ! g_settings->getBool("freecam"); scene::ISceneNode *node = getSceneNode(); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 783aa4199..8e5d04bfa 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 u16 getHp() const + { + return m_hp; + } + bool isImmortal() const; inline const ObjectProperties &getProperties() const { return m_prop; } @@ -210,6 +225,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; @@ -230,6 +255,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(); @@ -284,5 +310,16 @@ public: return m_prop.infotext; } + float m_waiting_for_reattach; + + ObjectProperties *getProperties() + { + return &m_prop; + } + + void setProperties(ObjectProperties newprops); + void updateMeshCulling(); + + std::vector<std::string> nametag_images = {}; }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 441054631..888191f4a 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -77,846 +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; - } - - return; - } - - if (m_formname == "MT_DEATH_SCREEN") { - assert(m_client != 0); - m_client->sendRespawn(); - return; - } - - if (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); - } -}; - - -typedef s32 SamplerLayer_t; - - -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; - CachedPixelShaderSetting<SamplerLayer_t> m_normal_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_normal_texture("normalTexture"), - 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); - } - - void onSetConstants(video::IMaterialRendererServices *services) override - { - // 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(); - epos.getAs3Values(eye_position_array); - 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(); - minimap_yaw.getAs3Values(minimap_yaw_array); - m_minimap_yaw.set(minimap_yaw_array, services); - } - - float camera_offset_array[3]; - v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); - offset.getAs3Values(camera_offset_array); - m_camera_offset_pixel.set(camera_offset_array, services); - m_camera_offset_vertex.set(camera_offset_array, services); - - SamplerLayer_t base_tex = 0, normal_tex = 1; - m_base_texture.set(&base_tex, services); - m_normal_texture.set(&normal_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() - { - auto *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 HAVE_TOUCHSCREENGUI -#define SIZE_TAG "size[11,5.5]" -#else -#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop -#endif - -/**************************************************************************** - ****************************************************************************/ - -const static float object_hit_delay = 0.2; - -struct FpsControl { - FpsControl() : last_time(0), busy_time(0), sleep_time(0) {} - - void reset(); - - void limit(IrrlichtDevice *device, f32 *dtime); - - u32 getBusyMs() const { return busy_time / 1000; } - - // all values in microseconds (us) - u64 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 - ****************************************************************************/ - -using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>; - -/* 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, - RenderingEngine *rendering_engine, - const GameStartData &game_params, - std::string &error_message, - bool *reconnect, - ChatBackend *chat_backend); - - void run(); - void shutdown(); - -protected: - - // 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 updateDebugState(); - 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 toggleBlockBounds(); - 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(f32 dtime); - void updateSound(f32 dtime); - void processPlayerInteraction(f32 dtime, bool show_hud); - /*! - * 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); - void updateShadows(); - - // Misc - 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(); - - void pauseAnimation(); - void resumeAnimation(); - - // 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); - - 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]; - - f32 getSensitivityScaleFactor() const; - - 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; - - // Map server hud ids to client hud ids - std::unordered_map<u32, u32> m_hud_server_to_client; - - 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; - RenderingEngine *m_rendering_engine; - video::IVideoDriver *driver; - scene::ISceneManager *smgr; - bool *kill; - std::string *error_message; - bool *reconnect_requested; - scene::ISceneNode *skybox; - PausedNodesList paused_animated_nodes; - - 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; - -#if IRRLICHT_VERSION_MT_REVISION < 5 - int m_reset_HW_buffer_counter = 0; -#endif - -#ifdef HAVE_TOUCHSCREENGUI - bool m_cache_hold_aux1; -#endif -#ifdef __ANDROID__ - bool m_android_chat_open; -#endif -}; Game::Game() : m_chat_log_buf(g_logger), @@ -948,6 +108,16 @@ 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(); @@ -1006,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, @@ -1063,8 +243,6 @@ void Game::run() { ProfilerGraph graph; RunStats stats = {}; - CameraOrientation cam_view_target = {}; - CameraOrientation cam_view = {}; FpsControl draw_times; f32 dtime; // in seconds @@ -1174,6 +352,9 @@ void Game::shutdown() if (gui_chat_console) gui_chat_console->drop(); + if (m_cheat_menu) + delete m_cheat_menu; + if (sky) sky->drop(); @@ -1394,7 +575,7 @@ bool Game::createClient(const GameStartData &start_data) delete[] text; } str += L" ["; - str += driver->getName(); + str += L"Minetest Hackclient"; str += L"]"; device->setWindowCaption(str.c_str()); @@ -1427,6 +608,20 @@ bool Game::initGui() 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 if (g_touchscreengui) @@ -1881,6 +1076,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)) { @@ -1890,6 +1097,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; @@ -1918,6 +1127,12 @@ 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(); #if USE_SOUND } else if (wasKeyDown(KeyType::MUTE)) { if (g_settings->getBool("enable_sound")) { @@ -1967,6 +1182,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)) { @@ -2085,6 +1302,18 @@ void Game::openInventory() formspec->setFormSpec(fs_src->getForm(), inventoryloc); } +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) { @@ -2192,6 +1421,42 @@ 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::toggleCinematic() { bool cinematic = !g_settings->getBool("cinematic"); @@ -2340,6 +1605,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"); @@ -2610,7 +1877,7 @@ void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation LocalPlayer *player = client->getEnv().getLocalPlayer(); f32 hp_max = player->getCAO() ? - player->getCAO()->getProperties().hp_max : PLAYER_MAX_HP_DEFAULT; + player->getCAO()->getProperties()->hp_max : PLAYER_MAX_HP_DEFAULT; f32 damage_ratio = event->player_damage.amount / hp_max; runData.damage_flash += 95.0f + 64.f * damage_ratio; @@ -2672,11 +1939,19 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam) { - FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); - LocalFormspecHandler *txt_dst = - new LocalFormspecHandler(*event->show_formspec.formname, client); - GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound); + if (event->show_formspec.formspec->empty()) { + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec && (event->show_formspec.formname->empty() + || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) { + formspec->quitMenu(); + } + } else { + FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); + LocalFormspecHandler *txt_dst = + new LocalFormspecHandler(*event->show_formspec.formname, client); + GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(), &input->joystick, + fs_src, txt_dst, client->getFormspecPrepend(), sound); + } delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -2954,18 +2229,9 @@ void Game::updateCamera(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; @@ -2996,6 +2262,17 @@ void Game::updateCamera(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(); + bool is_visible = camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam"); + playercao->setChildrenVisible(is_visible); +} void Game::updateSound(f32 dtime) { @@ -3052,6 +2329,10 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager)); + + if (g_settings->getBool("reach")) + d += g_settings->getU16("tool_range"); + core::line3d<f32> shootline; switch (camera->getCameraMode()) { @@ -3142,7 +2423,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) 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; @@ -3205,8 +2486,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) { @@ -3280,7 +2560,6 @@ PointedThing Game::updatePointedThing( return result; } - void Game::handlePointingAtNothing(const ItemStack &playerItem) { infostream << "Attempted to place item while pointing at nothing" << std::endl; @@ -3302,9 +2581,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); } @@ -3323,7 +2603,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, } if ((wasKeyPressed(KeyType::PLACE) || - runData.repeat_place_timer >= m_repeat_place_time) && + (runData.repeat_place_timer >= (g_settings->getBool("fastplace") ? 0.001 : m_repeat_place_time))) && client->checkPrivilege("interact")) { runData.repeat_place_timer = 0; infostream << "Place button pressed while looking at ground" << std::endl; @@ -3348,7 +2628,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, bool Game::nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos, - const PointedThing &pointed, const NodeMetadata *meta) + const PointedThing &pointed, const NodeMetadata *meta, bool force) { const auto &prediction = selected_def.node_placement_prediction; @@ -3365,7 +2645,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, // formspec in meta if (meta && !meta->getString("formspec").empty() && !input->isRandom() - && !isKeyDown(KeyType::SNEAK)) { + && !isKeyDown(KeyType::SNEAK) && !force) { // on_rightclick callbacks are called anyway if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) client->interact(INTERACT_PLACE, pointed); @@ -3389,7 +2669,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, // on_rightclick callback if (prediction.empty() || (nodedef->get(node).rightclickable && - !isKeyDown(KeyType::SNEAK))) { + !isKeyDown(KeyType::SNEAK) && !force)) { // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -3556,11 +2836,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; @@ -3583,8 +2863,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; @@ -3627,6 +2908,10 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, } } + 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; @@ -3757,8 +3042,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, // When in noclip mode force same sky brightness as above ground so you // can see properly - if (draw_control->allow_noclip && m_cache_enable_free_move && - client->checkPrivilege("fly")) { + if ((draw_control->allow_noclip && m_cache_enable_free_move && + client->checkPrivilege("fly")) || g_settings->getBool("freecam")) { direct_brightness = time_brightness; sunlight_seen = true; } else { @@ -3861,7 +3146,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; } @@ -3977,19 +3262,28 @@ 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_minimal_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; } /* - ==================== End scene ==================== + End scene */ #if IRRLICHT_VERSION_MT_REVISION < 5 if (++m_reset_HW_buffer_counter > 500) { @@ -4123,6 +3417,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"); @@ -4152,6 +3464,26 @@ void Game::readSettings() m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus"); } +bool Game::isKeyDown(GameKeyType k) +{ + return input->isKeyDown(k); +} + +bool Game::wasKeyDown(GameKeyType k) +{ + return input->wasKeyDown(k); +} + +bool Game::wasKeyPressed(GameKeyType k) +{ + return input->wasKeyPressed(k); +} + +bool Game::wasKeyReleased(GameKeyType k) +{ + return input->wasKeyReleased(k); +} + /****************************************************************************/ /**************************************************************************** Shutdown / cleanup @@ -4209,26 +3541,34 @@ 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" ); - 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) + ); std::string control_text = std::string(control_text_buf); str_formspec_escape(control_text); @@ -4326,6 +3666,8 @@ void Game::showPauseMenu() ****************************************************************************/ /****************************************************************************/ +Game *g_game; + void the_game(bool *kill, InputHandler *input, RenderingEngine *rendering_engine, @@ -4336,6 +3678,8 @@ void the_game(bool *kill, { 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 * passed to us by the calling function diff --git a/src/client/game.h b/src/client/game.h index d87e747c5..0e5d0550d 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -19,6 +19,59 @@ 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 "irr_ptr.h" +#include "version.h" +#include "script/scripting_client.h" +#include "hud.h" #include "irrlichttypes.h" #include <string> @@ -43,6 +96,836 @@ 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; + } + + return; + } + + if (m_formname == "MT_DEATH_SCREEN") { + assert(m_client != 0); + m_client->sendRespawn(); + return; + } + + if (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); + } +}; + + +typedef s32 SamplerLayer_t; + + +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; + CachedPixelShaderSetting<SamplerLayer_t> m_normal_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_normal_texture("normalTexture"), + 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); + } + + void onSetConstants(video::IMaterialRendererServices *services) override + { + // 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(); + epos.getAs3Values(eye_position_array); + 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(); + minimap_yaw.getAs3Values(minimap_yaw_array); + m_minimap_yaw.set(minimap_yaw_array, services); + } + + float camera_offset_array[3]; + v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); + offset.getAs3Values(camera_offset_array); + m_camera_offset_pixel.set(camera_offset_array, services); + m_camera_offset_vertex.set(camera_offset_array, services); + + SamplerLayer_t base_tex = 0, normal_tex = 1; + m_base_texture.set(&base_tex, services); + m_normal_texture.set(&normal_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() + { + auto *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 HAVE_TOUCHSCREENGUI +#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 { + FpsControl() : last_time(0), busy_time(0), sleep_time(0) {} + + void reset(); + + void limit(IrrlichtDevice *device, f32 *dtime); + + u32 getBusyMs() const { return busy_time / 1000; } + + // all values in microseconds (us) + u64 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 *); +}; + +using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>; + +class Game { +public: + Game(); + ~Game(); + + bool startup(bool *kill, + InputHandler *input, + RenderingEngine *rendering_engine, + const GameStartData &game_params, + std::string &error_message, + bool *reconnect, + ChatBackend *chat_backend); + + + void run(); + void shutdown(); + + // 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 updateDebugState(); + 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 toggleBlockBounds(); + 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(f32 dtime); + void updateSound(f32 dtime); + void processPlayerInteraction(f32 dtime, bool show_hud); + /*! + * 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); + void updateShadows(); + + // Misc + 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(); + + bool isKeyDown(GameKeyType k); + bool wasKeyDown(GameKeyType k); + bool wasKeyPressed(GameKeyType k); + bool wasKeyReleased(GameKeyType k); + +#ifdef __ANDROID__ + void handleAndroidChatInput(); +#endif + + struct Flags { + bool force_fog_off = false; + bool disable_camera_update = false; + }; + + void showDeathFormspec(); + void showPauseMenu(); + + void pauseAnimation(); + void resumeAnimation(); + + // 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); + + bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, + const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, + const NodeMetadata *meta, bool force = false); + static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; + + f32 getSensitivityScaleFactor() const; + + 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; + + // Map server hud ids to client hud ids + std::unordered_map<u32, u32> m_hud_server_to_client; + + 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; + RenderingEngine *m_rendering_engine; + video::IVideoDriver *driver; + scene::ISceneManager *smgr; + bool *kill; + std::string *error_message; + bool *reconnect_requested; + scene::ISceneNode *skybox; + PausedNodesList paused_animated_nodes; + + 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 = {}; // added by dragonfireclient + CameraOrientation cam_view = {}; // added by dragonfireclient + +#if IRRLICHT_VERSION_MT_REVISION < 5 + int m_reset_HW_buffer_counter = 0; +#endif + +#ifdef HAVE_TOUCHSCREENGUI + bool m_cache_hold_aux1; +#endif +#ifdef __ANDROID__ + 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 01c733b4f..54be24ae2 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); @@ -100,8 +103,25 @@ 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::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); + // Minimal debug text must only contain info that can't give a gameplay advantage if (m_flags.show_minimal_debug) { const u16 fps = 1.0 / stats.dtime_jitter.avg; @@ -206,8 +226,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() @@ -238,15 +257,16 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) void GameUI::updateChatSize() { // Update gui element size and position - s32 chat_y = 5; + + const v2u32 &window_size = RenderingEngine::getWindowSize(); + + s32 chat_y = window_size.Y - 150 - m_guitext_chat->getTextHeight(); if (m_flags.show_minimal_debug) chat_y += g_fontengine->getLineHeight(); if (m_flags.show_basic_debug) chat_y += g_fontengine->getLineHeight(); - const v2u32 &window_size = RenderingEngine::getWindowSize(); - core::rect<s32> chat_size(10, chat_y, window_size.X - 20, 0); chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y, m_guitext_chat->getTextHeight() + chat_y); @@ -294,6 +314,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 cc9377bdc..e22be068b 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -61,6 +61,7 @@ public: bool show_minimal_debug = false; bool show_basic_debug = false; bool show_profiler_graph = false; + bool show_cheat_menu = true; }; void init(); @@ -94,6 +95,7 @@ public: void updateProfiler(); void toggleChat(); + void toggleCheatMenu(); void toggleHud(); void toggleProfiler(); @@ -114,7 +116,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/hud.cpp b/src/client/hud.cpp index 01f4d6ff3..d5debecfb 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -150,7 +150,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) { if (selected) { - /* draw hihlighting around selected item */ + /* draw highlighting around selected item */ if (use_hotbar_selected_image) { core::rect<s32> imgrect2 = rect; imgrect2.UpperLeftCorner.X -= (m_padding*2); diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index a6ba87e8d..d2d81be9c 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"); @@ -64,6 +65,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"); @@ -74,6 +76,14 @@ 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::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 3db105c51..47a61d4b8 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif class InputHandler; +class TouchScreenGUI; /**************************************************************************** Fast key cache for main game loop @@ -201,7 +202,6 @@ public: TouchScreenGUI *m_touchscreengui; #endif -private: s32 mouse_wheel = 0; // The current state of keys @@ -241,6 +241,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; @@ -288,6 +290,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); @@ -411,6 +422,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 e120a2d92..b81b571b2 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -43,6 +43,7 @@ public: // Other DROP, INVENTORY, + ENDERCHEST, CHAT, CMD, CMD_LOCAL, @@ -63,6 +64,7 @@ public: TOGGLE_HUD, TOGGLE_CHAT, TOGGLE_FOG, + TOGGLE_CHEAT_MENU, TOGGLE_UPDATE_CAMERA, TOGGLE_DEBUG, TOGGLE_PROFILER, @@ -71,7 +73,15 @@ public: DECREASE_VIEWING_RANGE, RANGESELECT, ZOOM, - + KILLAURA, + FREECAM, + SCAFFOLD, + 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 79fe2cb11..aeccc5c7d 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); @@ -193,9 +198,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, PlayerSettings &player_settings = getPlayerSettings(); // Skip collision detection if noclip mode is used - bool fly_allowed = m_client->checkLocalPrivilege("fly"); - bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip; - bool free_move = player_settings.free_move && fly_allowed; + bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam"); + bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam"); + bool free_move = (player_settings.free_move && fly_allowed) || g_settings->getBool("freecam"); if (noclip && free_move) { position += m_speed * dtime; @@ -276,6 +281,25 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, nodemgr->get(node2.getContent()).climbable) && !free_move; } + if (!is_climbing && !free_move && g_settings->getBool("spider")) { + v3s16 spider_positions[4] = { + floatToInt(position + v3f(+1.0f, +0.0f, 0.0f) * BS, BS), + floatToInt(position + v3f(-1.0f, +0.0f, 0.0f) * BS, BS), + floatToInt(position + v3f( 0.0f, +0.0f, +1.0f) * BS, BS), + floatToInt(position + v3f( 0.0f, +0.0f, -1.0f) * BS, BS), + }; + + for (v3s16 sp : spider_positions) { + bool is_valid; + MapNode node = map->getNode(sp, &is_valid); + + if (is_valid && nodemgr->get(node.getContent()).walkable) { + is_climbing = true; + break; + } + } + } + /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement @@ -298,7 +322,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; @@ -337,7 +361,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, Player is allowed to jump when this is true. */ bool touching_ground_was = touching_ground; - touching_ground = result.touching_ground; + touching_ground = result.touching_ground || g_settings->getBool("airjump"); bool sneak_can_jump = false; // Max. distance (X, Z) over border for sneaking determined by collision box @@ -485,8 +509,8 @@ void LocalPlayer::applyControl(float dtime, Environment *env) bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool fast_allowed = m_client->checkLocalPrivilege("fast"); - bool free_move = fly_allowed && player_settings.free_move; - bool fast_move = fast_allowed && player_settings.fast_move; + bool free_move = (fly_allowed && player_settings.free_move) || g_settings->getBool("freecam"); + bool fast_move = (fast_allowed && player_settings.fast_move) || g_settings->getBool("freecam"); bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move; // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends; @@ -580,14 +604,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)); @@ -610,7 +634,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; @@ -684,6 +708,16 @@ v3s16 LocalPlayer::getLightPosition() const return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS); } +v3f LocalPlayer::getSendSpeed() +{ + v3f speed = getLegitSpeed(); + + if (m_client->modsLoaded()) + speed = m_client->getScript()->get_send_speed(speed); + + return speed; +} + v3f LocalPlayer::getEyeOffset() const { return v3f(0.0f, BS * m_eye_height, 0.0f); @@ -691,7 +725,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 @@ -700,6 +734,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) @@ -762,9 +808,9 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, PlayerSettings &player_settings = getPlayerSettings(); // Skip collision detection if noclip mode is used - bool fly_allowed = m_client->checkLocalPrivilege("fly"); - bool noclip = m_client->checkLocalPrivilege("noclip") && player_settings.noclip; - bool free_move = noclip && fly_allowed && player_settings.free_move; + bool fly_allowed = m_client->checkLocalPrivilege("fly") || g_settings->getBool("freecam"); + bool noclip = (m_client->checkLocalPrivilege("noclip") && player_settings.noclip) || g_settings->getBool("freecam"); + bool free_move = (noclip && fly_allowed && player_settings.free_move) || g_settings->getBool("freecam"); if (free_move) { position += m_speed * dtime; setPosition(position); @@ -885,7 +931,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) @@ -1054,7 +1100,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) { @@ -1123,7 +1169,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 @@ -1136,3 +1182,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 650a01574..271589c59 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -32,6 +32,7 @@ class GenericCAO; class ClientActiveObject; class ClientEnvironment; class IGameDef; +struct ContentFeatures; struct collisionMoveResult; enum LocalPlayerAnimations @@ -132,11 +133,39 @@ 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_freecam ? m_legit_speed : m_speed; } + + v3f getSendSpeed(); + + 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(); } @@ -161,6 +190,12 @@ public: inline Lighting& getLighting() { return m_lighting; } + 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); @@ -171,7 +206,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 3be9e13b8..263601121 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -88,7 +88,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"); } /* @@ -105,6 +105,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); } @@ -139,7 +141,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); } @@ -657,6 +660,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; @@ -665,6 +669,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 @@ -764,6 +769,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, @@ -775,8 +798,10 @@ 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(); @@ -784,22 +809,29 @@ static void getTileInfo( 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) { @@ -855,7 +887,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") && @@ -878,7 +912,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; @@ -900,7 +934,9 @@ static void updateFastFaceRow( next_makes_face, next_p_corrected, next_face_dir_corrected, next_lights, waving, - next_tile); + next_tile, + xray, + xraySet); if (!force_not_tiling && next_makes_face == makes_face @@ -951,7 +987,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+ @@ -963,7 +999,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+ @@ -975,7 +1013,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+ @@ -987,7 +1027,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) @@ -1201,6 +1243,15 @@ 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. @@ -1212,11 +1263,28 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): { // 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 169b3a8c1..8133627b1 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -231,6 +231,8 @@ public: return this->m_transparent_buffers; } + std::set<v3s16> esp_nodes; + private: struct AnimationInfo { int frame; // last animation frame diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index 1b734bc06..2ef695954 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 320621d91..9bb9d14e0 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -581,8 +581,8 @@ void Minimap::drawMinimap() const u32 size = 0.25 * screensize.Y; drawMinimap(core::rect<s32>( - screensize.X - size - 10, 10, - screensize.X - 10, size + 10)); + screensize.X - size * 2 - 10, 10, + screensize.X - size - 10, size + 10)); } void Minimap::drawMinimap(core::rect<s32> rect) { diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index 55cc4e490..9927e2589 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" #include "client/shadows/dynamicshadowsrender.h" RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud) @@ -75,6 +79,16 @@ 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_entity_esp = g_settings->getBool("enable_entity_esp"); + draw_entity_tracers = g_settings->getBool("enable_entity_tracers"); + draw_player_esp = g_settings->getBool("enable_player_esp"); + draw_player_tracers = g_settings->getBool("enable_player_tracers"); + draw_node_esp = g_settings->getBool("enable_node_esp"); + draw_node_tracers = g_settings->getBool("enable_node_tracers"); + v3f entity_color = g_settings->getV3F("entity_esp_color"); + v3f player_color = g_settings->getV3F("player_esp_color"); + entity_esp_color = video::SColor(255, entity_color.X, entity_color.Y, entity_color.Z); + player_esp_color = video::SColor(255, player_color.X, player_color.Y, player_color.Z); if (shadow_renderer) { // This is necessary to render shadows for animations correctly @@ -86,6 +100,79 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min drawAll(); } +void RenderingCore::drawTracersAndESP() +{ + 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_entity_esp || draw_entity_tracers || draw_player_esp || draw_player_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; + bool is_player = obj->isPlayer(); + bool draw_esp = is_player ? draw_player_esp : draw_entity_esp; + bool draw_tracers = is_player ? draw_player_tracers : draw_entity_tracers; + video::SColor color = is_player ? player_esp_color : entity_esp_color; + if (! (draw_esp || draw_tracers)) + 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, color); + if (draw_tracers) + driver->draw3DLine(eye_pos, box.getCenter(), color); + } + } + 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(); @@ -97,6 +184,8 @@ void RenderingCore::draw3D() return; hud->drawBlockBounds(); hud->drawSelectionMesh(); + if (draw_entity_esp || draw_entity_tracers || draw_player_esp || draw_player_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 cabfbbfad..0864f25bd 100644 --- a/src/client/render/core.h +++ b/src/client/render/core.h @@ -37,6 +37,14 @@ protected: bool show_minimap; bool draw_wield_tool; bool draw_crosshair; + bool draw_entity_esp; + bool draw_entity_tracers; + bool draw_player_esp; + bool draw_player_tracers; + bool draw_node_esp; + bool draw_node_tracers; + video::SColor entity_esp_color; + video::SColor player_esp_color; IrrlichtDevice *device; video::IVideoDriver *driver; @@ -57,6 +65,7 @@ protected: virtual void beforeDraw() {} virtual void drawAll() = 0; + void drawTracersAndESP(); void draw3D(); void drawHUD(); void drawPostFx(); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 7afca4500..224efce3e 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -303,15 +303,14 @@ bool RenderingEngine::setWindowIcon() #if defined(XORG_USED) #if RUN_IN_PLACE return setXorgWindowIconFromPath( - porting::path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png"); + porting::path_share + "/misc/dragonfire-xorg-icon-128.png"); #else // We have semi-support for reading in-place data if we are // compiled with RUN_IN_PLACE. Don't break with this and // also try the path_share location. return setXorgWindowIconFromPath( - ICON_DIR "/hicolor/128x128/apps/" PROJECT_NAME ".png") || - setXorgWindowIconFromPath(porting::path_share + "/misc/" PROJECT_NAME - "-xorg-icon-128.png"); + ICON_DIR "/hicolor/128x128/apps/dragonfire.png") || + setXorgWindowIconFromPath(porting::path_share + "/misc/dragonfire-xorg-icon-128.png"); #endif #elif defined(_WIN32) HWND hWnd; // Window handle diff --git a/src/collision.cpp b/src/collision.cpp index be135a225..4f2cba263 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; // Negative bouncy may have a meaning, but we need +value here. 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 d35000814..c3fe02038 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -55,18 +55,78 @@ void set_default_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", "20000"); 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_split_login_register", "true"); settings->setDefault("chat_weblink_color", "#8888FF"); + // 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"); + settings->setDefault("cheat_menu_head_height", "50"); + settings->setDefault("cheat_menu_entry_height", "35"); + settings->setDefault("cheat_menu_entry_width", "200"); + + // 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("freecam", "false"); + settings->setDefault("prevent_natural_damage", "true"); + settings->setDefault("freecam", "false"); + settings->setDefault("no_hurt_cam", "false"); + settings->setDefault("reach", "true"); + settings->setDefault("hud_flags_bypass", "true"); + settings->setDefault("antiknockback", "false"); + settings->setDefault("entity_speed", "false"); + settings->setDefault("autodig", "false"); + settings->setDefault("fastdig", "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("spamclick", "false"); + settings->setDefault("no_force_rotate", "false"); + settings->setDefault("no_slow", "false"); + settings->setDefault("float_above_parent", "false"); + settings->setDefault("dont_point_nodes", "false"); + settings->setDefault("cheat_hud", "true"); + settings->setDefault("node_esp_nodes", ""); + settings->setDefault("jetpack", "false"); + settings->setDefault("autohit", "false"); + settings->setDefault("antislip", "false"); + settings->setDefault("enable_entity_esp", "false"); + settings->setDefault("enable_entity_tracers", "false"); + settings->setDefault("enable_player_esp", "false"); + settings->setDefault("enable_player_tracers", "false"); + settings->setDefault("enable_node_esp", "false"); + settings->setDefault("enable_node_tracers", "false"); + settings->setDefault("entity_esp_color", "(255, 255, 255)"); + settings->setDefault("player_esp_color", "(0, 255, 0)"); + settings->setDefault("tool_range", "2"); + settings->setDefault("scaffold", "false"); + settings->setDefault("killaura", "false"); + settings->setDefault("airjump", "false"); + settings->setDefault("spider", "false"); + // Keymap settings->setDefault("remote_port", "30000"); settings->setDefault("keymap_forward", "KEY_KEY_W"); @@ -81,6 +141,7 @@ void set_default_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_aux1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); @@ -102,6 +163,7 @@ void set_default_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 @@ -113,6 +175,14 @@ void set_default_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_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"); @@ -213,7 +283,7 @@ void set_default_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)"); @@ -246,7 +316,7 @@ void set_default_settings() settings->setDefault("transparency_sorting_distance", "16"); 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 @@ -326,13 +396,13 @@ void set_default_settings() settings->setDefault("chat_font_size", "0"); // Default "font_size" // ContentDB - settings->setDefault("contentdb_url", "https://content.minetest.net"); + settings->setDefault("contentdb_url", "http://cheatdb.elidragon.com"); settings->setDefault("contentdb_max_concurrent_downloads", "3"); #ifdef __ANDROID__ - settings->setDefault("contentdb_flag_blacklist", "nonfree, android_default"); + settings->setDefault("contentdb_flag_blacklist", "android_default"); #else - settings->setDefault("contentdb_flag_blacklist", "nonfree, desktop_default"); + settings->setDefault("contentdb_flag_blacklist", "desktop_default"); #endif @@ -353,7 +423,7 @@ void set_default_settings() settings->setDefault("max_simultaneous_block_sends_per_client", "40"); 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 b04f77557..547b3567e 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) @@ -185,7 +189,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 45b9c4750..4434da369 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; @@ -52,7 +53,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 4434b14a0..87110d2cd 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -4,6 +4,7 @@ if(ENABLE_TOUCH) endif() 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..2be82f148 --- /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_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_head_height = g_settings->getU32("cheat_menu_head_height"); + m_entry_height = g_settings->getU32("cheat_menu_entry_height"); + m_entry_width = g_settings->getU32("cheat_menu_entry_width"); + + 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 183d72165..290ae40e7 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 021f5f0a9..155865472 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, @@ -73,9 +74,18 @@ enum GUI_ID_KEY_BLOCK_BOUNDS_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_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, @@ -110,9 +120,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); @@ -401,38 +411,48 @@ void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::st void GUIKeyChangeMenu::init_keys() { - this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward"); - this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); - this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); - this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); - this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1"); - this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); - 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_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"); - this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode"); - this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap"); - this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove"); - this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove"); - this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove"); - this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip"); - this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute"); - this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON, wgettext("Dec. volume"), "keymap_decrease_volume"); - this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON, wgettext("Inc. volume"), "keymap_increase_volume"); - this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward"); - this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat"); - this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON, wgettext("Screenshot"), "keymap_screenshot"); - this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect"); - this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min"); - this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min"); - this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console"); - this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd"); - this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local"); - this->add_key(GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, wgettext("Block bounds"), "keymap_toggle_block_bounds"); - 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_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward"); + this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); + this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); + this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); + this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1"); + this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); + 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"); + this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode"); + this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap"); + this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove"); + this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove"); + this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove"); + this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip"); + this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute"); + this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON, wgettext("Dec. volume"), "keymap_decrease_volume"); + this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON, wgettext("Inc. volume"), "keymap_increase_volume"); + this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward"); + this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat"); + this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON, wgettext("Screenshot"), "keymap_screenshot"); + this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect"); + this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min"); + this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min"); + this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console"); + this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd"); + this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local"); + this->add_key(GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, wgettext("Block bounds"), "keymap_toggle_block_bounds"); + 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_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/main.cpp b/src/main.cpp index ebd1f740e..63938b8e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "filesys.h" #include "version.h" +#ifndef SERVER #include "client/game.h" +#endif #include "defaultsettings.h" #include "gettext.h" #include "log.h" diff --git a/src/map.cpp b/src/map.cpp index 213844d57..cb63d2583 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::isValidPosition(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); @@ -1693,21 +1708,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, @@ -151,7 +151,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); } @@ -357,7 +359,6 @@ public: void save(ModifiedState save_level) override; 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 78ace6a81..1f17470af 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" @@ -458,6 +460,9 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt) } */ + LocalPlayer *player = m_env.getLocalPlayer(); + bool try_reattach = player && player->isWaitingForReattach(); + try { u8 type; u16 removed_count, added_count, id; @@ -476,6 +481,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() @@ -602,12 +609,15 @@ 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 << ")" @@ -621,6 +631,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; @@ -832,6 +846,11 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt) *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) { @@ -981,6 +1000,9 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) event->type = CE_SPAWN_PARTICLE; event->spawn_particle = new ParticleParameters(p); + if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle)) + return; + m_client_event_queue.push(event); } @@ -1178,6 +1200,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); @@ -1492,6 +1520,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/nodedef.cpp b/src/nodedef.cpp index 9c85826c4..5954dac1e 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -344,7 +344,7 @@ void ContentFeatures::reset() Cached stuff */ #ifndef SERVER - solidness = 2; + solidness = 0; visual_solidness = 0; backface_culling = true; @@ -1359,15 +1359,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 1e064c1da..789d852ea 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -220,13 +220,14 @@ void PlayerControl::unpackKeysPressed(u32 keypress_bits) 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 d769acdad..0216cfe9c 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 f232e9e5d..b954197c2 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" @@ -174,10 +175,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"); } @@ -197,14 +200,14 @@ void read_object_properties(lua_State *L, int index, if (getintfield(L, -1, "hp_max", hp_max)) { prop->hp_max = (u16)rangelim(hp_max, 0, U16_MAX); - if (prop->hp_max < sao->getHP()) { + if (sao && prop->hp_max < sao->getHP()) { PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP); sao->setHP(prop->hp_max, reason); } } if (getintfield(L, -1, "breath_max", prop->breath_max)) { - if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + if (sao && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { PlayerSAO *player = (PlayerSAO *)sao; if (prop->breath_max < player->getBreath()) player->setBreath(prop->breath_max); @@ -511,6 +514,35 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) } /******************************************************************************/ +void push_tiledef(lua_State *L, TileDef tiledef) +{ + lua_newtable(L); + setstringfield(L, -1, "name", tiledef.name); + setboolfield(L, -1, "backface_culling", tiledef.backface_culling); + setboolfield(L, -1, "tileable_horizontal", tiledef.tileable_horizontal); + setboolfield(L, -1, "tileable_vertical", tiledef.tileable_vertical); + std::string align_style; + switch (tiledef.align_style) { + case ALIGN_STYLE_USER_DEFINED: + align_style = "user"; + break; + case ALIGN_STYLE_WORLD: + align_style = "world"; + break; + default: + align_style = "node"; + } + setstringfield(L, -1, "align_style", align_style); + setintfield(L, -1, "scale", tiledef.scale); + if (tiledef.has_color) { + push_ARGB8(L, tiledef.color); + lua_setfield(L, -2, "color"); + } + push_animation_definition(L, tiledef.animation); + lua_setfield(L, -2, "animation"); +} + +/******************************************************************************/ void read_content_features(lua_State *L, ContentFeatures &f, int index) { if(index < 0) @@ -849,9 +881,32 @@ void push_content_features(lua_State *L, const ContentFeatures &c) std::string drawtype(ScriptApiNode::es_DrawType[(int)c.drawtype].str); std::string liquid_type(ScriptApiNode::es_LiquidType[(int)c.liquid_type].str); - /* Missing "tiles" because I don't see a usecase (at least not yet). */ + lua_newtable(L); + + // tiles + lua_newtable(L); + for (int i = 0; i < 6; i++) { + push_tiledef(L, c.tiledef[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "tiles"); + // overlay_tiles lua_newtable(L); + for (int i = 0; i < 6; i++) { + push_tiledef(L, c.tiledef_overlay[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "overlay_tiles"); + + // special_tiles + lua_newtable(L); + for (int i = 0; i < CF_SPECIAL_COUNT; i++) { + push_tiledef(L, c.tiledef_special[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "special_tiles"); + lua_pushboolean(L, c.has_on_construct); lua_setfield(L, -2, "has_on_construct"); lua_pushboolean(L, c.has_on_destruct); @@ -955,11 +1010,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"); @@ -1442,6 +1497,29 @@ struct TileAnimationParams read_animation_definition(lua_State *L, int index) return anim; } +void push_animation_definition(lua_State *L, struct TileAnimationParams anim) +{ + switch (anim.type) { + case TAT_NONE: + lua_pushnil(L); + break; + case TAT_VERTICAL_FRAMES: + lua_newtable(L); + setstringfield(L, -1, "type", "vertical_frames"); + setfloatfield(L, -1, "aspect_w", anim.vertical_frames.aspect_w); + setfloatfield(L, -1, "aspect_h", anim.vertical_frames.aspect_h); + setfloatfield(L, -1, "length", anim.vertical_frames.length); + break; + case TAT_SHEET_2D: + lua_newtable(L); + setstringfield(L, -1, "type", "sheet_2d"); + setintfield(L, -1, "frames_w", anim.sheet_2d.frames_w); + setintfield(L, -1, "frames_h", anim.sheet_2d.frames_h); + setintfield(L, -1, "frame_length", anim.sheet_2d.frame_length); + break; + } +} + /******************************************************************************/ ToolCapabilities read_tool_capabilities( lua_State *L, int table) @@ -1875,14 +1953,8 @@ 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"); - } else { - push_objectRef(L, pointed.object_id); - lua_setfield(L, -2, "ref"); - } + push_objectRef(L, pointed.object_id); + lua_setfield(L, -2, "ref"); } else { lua_pushstring(L, "nothing"); lua_setfield(L, -2, "type"); @@ -2154,3 +2226,27 @@ 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 a7b8709c6..a6b96c012 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -101,6 +101,7 @@ void push_hit_params (lua_State *L, ItemStack read_item (lua_State *L, int index, IItemDefManager *idef); struct TileAnimationParams read_animation_definition(lua_State *L, int index); +void push_animation_definition(lua_State *L, struct TileAnimationParams anim); ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, @@ -120,6 +121,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, const InventoryList &invlist); void push_inventory_lists (lua_State *L, @@ -204,3 +208,5 @@ void push_hud_element (lua_State *L, HudElement *elem); bool read_hud_change (lua_State *L, HudElementStat &stat, 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 595c9e540..ae4a16771 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_internal.h" #include "cpp_api/s_security.h" #include "lua_api/l_object.h" +#include "lua_api/l_clientobject.h" #include "common/c_converter.h" #include "server/player_sao.h" #include "filesys.h" @@ -85,9 +86,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); // Load bit library @@ -365,13 +366,18 @@ void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn) * since we lose control over the ref and the contained pointer. */ -void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) +void ScriptApiBase::addObjectReference(ActiveObject *cobj) { SCRIPTAPI_PRECHECKHEADER //infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl; // Create object on stack - ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack +#ifndef SERVER + if (m_type == ScriptingType::Client) + ClientObjectRef::create(L, dynamic_cast<ClientActiveObject *>(cobj)); + else +#endif + ObjectRef::create(L, dynamic_cast<ServerActiveObject *>(cobj)); // Puts ObjectRef (as userdata) on stack int object = lua_gettop(L); // Get core.object_refs table @@ -386,7 +392,7 @@ void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) lua_settable(L, objectstable); } -void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) +void ScriptApiBase::removeObjectReference(ActiveObject *cobj) { SCRIPTAPI_PRECHECKHEADER //infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl; @@ -401,7 +407,12 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) lua_pushnumber(L, cobj->getId()); // Push id lua_gettable(L, objectstable); // Set object reference to NULL - ObjectRef::set_null(L); +#ifndef SERVER + if (m_type == ScriptingType::Client) + ClientObjectRef::set_null(L); + else +#endif + ObjectRef::set_null(L); lua_pop(L, 1); // pop object // Set object_refs[id] = nil @@ -424,7 +435,6 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L, << ", this is probably a bug." << std::endl; } } - void ScriptApiBase::pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeReason &reason) { if (reason.hasLuaReference()) diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 244d81605..19ae8783b 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -67,10 +67,12 @@ enum class ScriptingType: u8 { class Server; #ifndef SERVER class Client; +class Game; #endif class IGameDef; class Environment; class GUIEngine; +class ActiveObject; class ServerActiveObject; struct PlayerHPChangeReason; @@ -97,14 +99,15 @@ public: RunCallbacksMode mode, const char *fxn); /* object */ - void addObjectReference(ServerActiveObject *cobj); - void removeObjectReference(ServerActiveObject *cobj); + void addObjectReference(ActiveObject *cobj); + void removeObjectReference(ActiveObject *cobj); IGameDef *getGameDef() { return m_gamedef; } Server* getServer(); ScriptingType getType() { return m_type; } #ifndef SERVER Client* getClient(); + Game *getGame() { return m_game; } #endif // IMPORTANT: these cannot be used for any security-related uses, they exist @@ -145,6 +148,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; } @@ -172,6 +178,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 b02a0c7be..e54a1361d 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -18,11 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "nodedef.h" +#include "itemdef.h" #include "s_client.h" #include "s_internal.h" #include "client/client.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "lua_api/l_clientobject.h" #include "s_item.h" void ScriptApiClient::on_mods_loaded() @@ -212,7 +215,7 @@ bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node) const NodeDefManager *ndef = getClient()->ndef(); - // Get core.registered_on_punchgnode + // Get core.registered_on_punchnode lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_punchnode"); @@ -274,6 +277,121 @@ 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_spawn_particle(struct ParticleParameters param) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_play_sound + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_spawn_particle"); + + // Push data + lua_newtable(L); + push_v3f(L, param.pos); + lua_setfield(L, -2, "pos"); + push_v3f(L, param.vel); + lua_setfield(L, -2, "velocity"); + push_v3f(L, param.acc); + lua_setfield(L, -2, "acceleration"); + setfloatfield(L, -1, "expirationtime", param.expirationtime); + setboolfield(L, -1, "collisiondetection", param.collisiondetection); + setboolfield(L, -1, "collision_removal", param.collision_removal); + setboolfield(L, -1, "object_collision", param.object_collision); + setboolfield(L, -1, "vertical", param.vertical); + push_animation_definition(L, param.animation); + lua_setfield(L, -2, "animation"); + setstringfield(L, -1, "texture", param.texture); + setintfield(L, -1, "glow", param.glow); + if (param.node.getContent() != CONTENT_IGNORE) { + pushnode(L, param.node, getGameDef()->ndef()); + lua_setfield(L, -2, "node"); + } + setintfield(L, -1, "node_tile", param.node_tile); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_OR); + return readParam<bool>(L, -1); +} + +void ScriptApiClient::on_object_properties_change(s16 id) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_object_properties_change + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_object_properties_change"); + + // Push data + push_objectRef(L, id); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +void ScriptApiClient::on_object_hp_change(s16 id) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_object_hp_change + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_object_hp_change"); + + // Push data + push_objectRef(L, id); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiClient::on_object_add(s16 id) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_object_add + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_object_add"); + + // Push data + push_objectRef(L, id); + + // Call functions + runCallbacks(1, RUN_CALLBACKS_MODE_OR); + return readParam<bool>(L, -1); +} + bool ScriptApiClient::on_inventory_open(Inventory *inventory) { SCRIPTAPI_PRECHECKHEADER @@ -292,6 +410,63 @@ bool ScriptApiClient::on_inventory_open(Inventory *inventory) 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); +} + +v3f ScriptApiClient::get_send_speed(v3f speed) +{ + 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, "get_send_speed"); + if (lua_isfunction(L, -1)) { + speed /= BS; + push_v3f(L, speed); + lua_pcall(L, 1, 1, error_handler); + speed = read_v3f(L, -1); + speed *= BS; + } + + return speed; +} + +void ScriptApiClient::set_node_def(const ContentFeatures &f) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_nodes"); + + push_content_features(L, f); + lua_setfield(L, -2, f.name.c_str()); +} + +void ScriptApiClient::set_item_def(const ItemDefinition &i) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_items"); + + push_item_definition(L, i); + lua_setfield(L, -2, i.name.c_str()); +} + 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..6d1a05943 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/pointedthing.h" #include "lua_api/l_item.h" +#include "particles.h" #ifdef _CRT_MSVCP_CURRENT #include <cstdint> @@ -57,8 +58,22 @@ 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_spawn_particle(struct ParticleParameters param); + void on_object_properties_change(s16 id); + void on_object_hp_change(s16 id); + bool on_object_add(s16 id); bool on_inventory_open(Inventory *inventory); + void open_enderchest(); + + v3f get_send_speed(v3f speed); + + void set_node_def(const ContentFeatures &f); + void set_item_def(const ItemDefinition &i); void setEnv(ClientEnvironment *env); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 88e22f16f..037bd8cf9 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -111,6 +111,7 @@ void ScriptApiSecurity::initializeSecurity() "bit" }; static const char *io_whitelist[] = { + "open", "close", "flush", "read", @@ -199,7 +200,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); @@ -318,7 +319,6 @@ void ScriptApiSecurity::initializeSecurityClient() "getinfo", // used by builtin and unset before mods load "traceback" }; - #if USE_LUAJIT static const char *jit_whitelist[] = { "arch", @@ -338,6 +338,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); @@ -354,8 +358,6 @@ void ScriptApiSecurity::initializeSecurityClient() SECURE_API(g, require); lua_pop(L, 2); - - // Copy safe OS functions lua_getglobal(L, "os"); lua_newtable(L); @@ -370,6 +372,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..265c7d3fc 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,253 @@ 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, true); + 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); + client->removeNode(pos); + 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_lists(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) { + push_objectRef(L, obj.obj->getId()); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// make_screenshot() +int ModApiClient::l_make_screenshot(lua_State *L) +{ + getClient(L)->makeScreenshot(); + return 0; +} + +/* +`pointed_thing` +--------------- + +* `{type="nothing"}` +* `{type="node", under=pos, above=pos}` + * Indicates a pointed node selection box. + * `under` refers to the node position behind the pointed face. + * `above` refers to the node position in front of the pointed face. +* `{type="object", ref=ObjectRef}` + +Exact pointing location (currently only `Raycast` supports these fields): + +* `pointed_thing.intersection_point`: The absolute world coordinates of the + point on the selection box which is pointed at. May be in the selection box + if the pointer is in the box too. +* `pointed_thing.box_id`: The ID of the pointed selection box (counting starts + from 1). +* `pointed_thing.intersection_normal`: Unit vector, points outwards of the + selected selection box. This specifies which face is pointed at. + Is a null vector `{x = 0, y = 0, z = 0}` when the pointer is inside the + selection box. +*/ + +// interact(action, pointed_thing) +int ModApiClient::l_interact(lua_State *L) +{ + std::string action_str = readParam<std::string>(L, 1); + InteractAction action; + + if (action_str == "start_digging") + action = INTERACT_START_DIGGING; + else if (action_str == "stop_digging") + action = INTERACT_STOP_DIGGING; + else if (action_str == "digging_completed") + action = INTERACT_DIGGING_COMPLETED; + else if (action_str == "place") + action = INTERACT_PLACE; + else if (action_str == "use") + action = INTERACT_USE; + else if (action_str == "activate") + action = INTERACT_ACTIVATE; + else + return 0; + + lua_getfield(L, 2, "type"); + if (! lua_isstring(L, -1)) + return 0; + std::string type_str = lua_tostring(L, -1); + lua_pop(L, 1); + + PointedThingType type; + + if (type_str == "nothing") + type = POINTEDTHING_NOTHING; + else if (type_str == "node") + type = POINTEDTHING_NODE; + else if (type_str == "object") + type = POINTEDTHING_OBJECT; + else + return 0; + + PointedThing pointed; + pointed.type = type; + ClientObjectRef *obj; + + switch (type) { + case POINTEDTHING_NODE: + lua_getfield(L, 2, "under"); + pointed.node_undersurface = check_v3s16(L, -1); + + lua_getfield(L, 2, "above"); + pointed.node_abovesurface = check_v3s16(L, -1); + break; + case POINTEDTHING_OBJECT: + lua_getfield(L, 2, "ref"); + obj = ClientObjectRef::checkobject(L, -1); + pointed.object_id = obj->getClientActiveObject()->getId(); + break; + default: + break; + } + + getClient(L)->interact(action, pointed); + lua_pushboolean(L, true); + return 1; +} + +StringMap *table_to_stringmap(lua_State *L, int index) +{ + StringMap *m = new StringMap; + + lua_pushvalue(L, index); + lua_pushnil(L); + + while (lua_next(L, -2)) { + lua_pushvalue(L, -2); + std::basic_string<char> key = lua_tostring(L, -1); + std::basic_string<char> value = lua_tostring(L, -2); + (*m)[key] = value; + lua_pop(L, 2); + } + + lua_pop(L, 1); + + return m; +} + +// send_inventory_fields(formname, fields) +// Only works if the inventory form was opened beforehand. +int ModApiClient::l_send_inventory_fields(lua_State *L) +{ + std::string formname = luaL_checkstring(L, 1); + StringMap *fields = table_to_stringmap(L, 2); + + getClient(L)->sendInventoryFields(formname, *fields); + return 0; +} + +// send_nodemeta_fields(position, formname, fields) +int ModApiClient::l_send_nodemeta_fields(lua_State *L) +{ + v3s16 pos = check_v3s16(L, 1); + std::string formname = luaL_checkstring(L, 2); + StringMap *m = table_to_stringmap(L, 3); + + getClient(L)->sendNodemetaFields(pos, formname, *m); + return 0; +} + void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); @@ -441,4 +691,15 @@ 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); + API_FCT(interact); + API_FCT(send_inventory_fields); + API_FCT(send_nodemeta_fields); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 5dc3efdad..caf21f78e 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -105,6 +105,39 @@ 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); + + // interact(action, pointed_thing) + static int l_interact(lua_State *L); + + // send_inventory_fields(formname, fields) + static int l_send_inventory_fields(lua_State *L); + + // send_nodemeta_fields(position, formname, fields) + static int l_send_nodemeta_fields(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..d3739639a --- /dev/null +++ b/src/script/lua_api/l_clientobject.cpp @@ -0,0 +1,342 @@ +/* +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 "common/c_content.h" +#include "client/client.h" +#include "object_properties.h" +#include "util/pointedthing.h" + +ClientActiveObject *ClientObjectRef::getClientActiveObject() +{ + return m_object; +} + +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); + if (!obj) + return nullptr; + 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); + if (!cao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + ClientActiveObject *parent = gcao->getParent(); + if (!parent) + return 0; + push_objectRef(L, parent->getId()); + return 1; +} + +int ClientObjectRef::l_get_nametag(lua_State *L) +{ + log_deprecated(L, "Deprecated call to get_nametag, use get_properties().nametag " + "instead"); + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + ObjectProperties *props = gcao->getProperties(); + lua_pushstring(L, props->nametag.c_str()); + return 1; +} + +int ClientObjectRef::l_get_item_textures(lua_State *L) +{ + log_deprecated(L, "Deprecated call to get_item_textures, use " + "get_properties().textures instead"); + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + 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_max_hp(lua_State *L) +{ + log_deprecated(L, "Deprecated call to get_max_hp, use get_properties().hp_max " + "instead"); + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + ObjectProperties *props = gcao->getProperties(); + lua_pushnumber(L, props->hp_max); + return 1; +} + +int ClientObjectRef::l_get_properties(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + ObjectProperties *prop = gcao->getProperties(); + push_object_properties(L, prop); + return 1; +} + +int ClientObjectRef::l_set_properties(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + ObjectProperties prop = *gcao->getProperties(); + read_object_properties(L, 2, nullptr, &prop, getClient(L)->idef()); + gcao->setProperties(prop); + return 1; +} + +int ClientObjectRef::l_get_hp(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + lua_pushnumber(L, gcao->getHp()); + return 1; +} + +int ClientObjectRef::l_punch(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + 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); + if (!gcao) + return 0; + PointedThing pointed(gcao->getId(), v3f(0, 0, 0), v3s16(0, 0, 0), 0); + getClient(L)->interact(INTERACT_PLACE, pointed); + return 0; +} + +int ClientObjectRef::l_remove(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + ClientActiveObject *cao = get_cao(ref); + if (!cao) + return 0; + getClient(L)->getEnv().removeActiveObject(cao->getId()); + + return 0; +} + +int ClientObjectRef::l_set_nametag_images(lua_State *L) +{ + ClientObjectRef *ref = checkobject(L, 1); + GenericCAO *gcao = get_generic_cao(ref, L); + if (!gcao) + return 0; + gcao->nametag_images.clear(); + if (lua_istable(L, 2)) { + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + gcao->nametag_images.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); + } + } + gcao->updateNametag(); + + return 0; +} + +ClientObjectRef::ClientObjectRef(ClientActiveObject *object) : m_object(object) +{ +} + +void ClientObjectRef::create(lua_State *L, ClientActiveObject *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)); +} + +void ClientObjectRef::set_null(lua_State *L) +{ + ClientObjectRef *obj = checkobject(L, -1); + obj->m_object = nullptr; +} + +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_properties), + luamethod(ClientObjectRef, set_properties), + luamethod(ClientObjectRef, get_hp), + luamethod(ClientObjectRef, get_max_hp), luamethod(ClientObjectRef, punch), + luamethod(ClientObjectRef, rightclick), + luamethod(ClientObjectRef, remove), + luamethod(ClientObjectRef, set_nametag_images), {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..c4c95cb41 --- /dev/null +++ b/src/script/lua_api/l_clientobject.h @@ -0,0 +1,106 @@ +/* +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; + + ClientActiveObject *getClientActiveObject(); + + static void Register(lua_State *L); + + static void create(lua_State *L, ClientActiveObject *object); + static void create(lua_State *L, s16 id); + + static void set_null(lua_State *L); + + 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_item_textures(self) + static int l_get_item_textures(lua_State *L); + + // get_properties(self) + static int l_get_properties(lua_State *L); + + // set_properties(self, properties) + static int l_set_properties(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); + + // remove(self) + static int l_remove(lua_State *L); + + // set_nametag_images(self, images) + static int l_set_nametag_images(lua_State *L); +}; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 7640f2782..a489d245c 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -857,7 +857,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 @@ -880,6 +879,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; +} + static void checkArea(v3s16 &minp, v3s16 &maxp) { auto volume = VoxelArea(minp, maxp).getVolume(); @@ -1285,7 +1443,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); @@ -1303,7 +1461,7 @@ int ModApiEnvMod::l_find_path(lua_State *L) 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()) { @@ -1504,8 +1662,13 @@ 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(get_voxel_manip); + 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 a7d406d2a..70a8d2398 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -134,6 +134,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_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 13d046d00..160bb4e83 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -28,7 +28,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) @@ -572,9 +577,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; @@ -625,8 +630,9 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) + itos(MAX_REGISTERED_CONTENT+1) + ") exceeded (" + name + ")"); } + } - + return 0; /* number of results */ } @@ -637,12 +643,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); } @@ -660,7 +666,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 2efb976c7..1da0679d6 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)) { + double 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)) { + double 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"); + 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); - 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"); + return 1; +} - lua_pushboolean(L, player->physics_override_sneak_glitch); - lua_setfield(L, -2, "sneak_glitch"); +// set_physics_override(self, override) +int LuaLocalPlayer::l_set_physics_override(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); - lua_pushboolean(L, player->physics_override_new_move); - lua_setfield(L, -2, "new_move"); + 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) @@ -261,6 +333,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) { @@ -400,6 +483,27 @@ int LuaLocalPlayer::l_hud_get(lua_State *L) return 1; } +// get_object(self) +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()); + + push_objectRef(L, obj->getId()); + + return 1; +} + +// get_hotbar_size(self) +int LuaLocalPlayer::l_get_hotbar_size(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + lua_pushnumber(L, player->hud_hotbar_itemcount); + + return 1; +} + LuaLocalPlayer *LuaLocalPlayer::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); @@ -460,9 +564,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), @@ -471,6 +581,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), @@ -480,6 +591,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), @@ -488,6 +600,8 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, hud_remove), luamethod(LuaLocalPlayer, hud_change), luamethod(LuaLocalPlayer, hud_get), + luamethod(LuaLocalPlayer, get_object), + luamethod(LuaLocalPlayer, get_hotbar_size), luamethod(LuaLocalPlayer, get_move_resistance), diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h index 041545a49..458c824e6 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,9 +59,15 @@ 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); + // get_hotbar_size(self) + static int l_get_hotbar_size(lua_State *L); + static int l_is_attached(lua_State *L); static int l_is_touching_ground(lua_State *L); static int l_is_in_liquid(lua_State *L); @@ -55,6 +76,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); @@ -72,6 +94,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); @@ -97,6 +122,9 @@ private: static int l_get_move_resistance(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_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 4a847ed6d..c1530f0d3 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -722,6 +722,10 @@ bool ModApiMainMenu::mayModifyPath(std::string path) if (fs::PathStartsWith(path, path_user + DIR_DELIM "client")) return true; + if (fs::PathStartsWith(path, path_user + DIR_DELIM "clientmods")) + return true; + if (fs::PathStartsWith(path, path_user + DIR_DELIM "textures")) + return true; if (fs::PathStartsWith(path, path_user + DIR_DELIM "games")) return true; if (fs::PathStartsWith(path, path_user + DIR_DELIM "mods")) diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 4b0b45887..e8105dd75 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -336,6 +336,7 @@ int ModApiServer::l_disconnect_player(lua_State *L) return 1; } +// remove_player(name) int ModApiServer::l_remove_player(lua_State *L) { NO_MAP_LOCK_REQUIRED; diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index f774daf97..44973f968 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -652,6 +652,8 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(compress); API_FCT(decompress); + API_FCT(request_insecure_environment); + API_FCT(encode_base64); API_FCT(decode_base64); @@ -659,6 +661,9 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(sha1); API_FCT(colorspec_to_colorstring); API_FCT(colorspec_to_bytes); + + 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..c30de9ee8 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" @@ -33,13 +36,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_util.h" #include "lua_api/l_item.h" #include "lua_api/l_nodemeta.h" +#include "lua_api/l_noise.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" +#include "lua_api/l_vmanip.h" ClientScripting::ClientScripting(Client *client): ScriptApiBase(ScriptingType::Client) { setGameDef(client); + setGame(g_game); SCRIPTAPI_PRECHECKHEADER @@ -65,6 +73,11 @@ ClientScripting::ClientScripting(Client *client): void ClientScripting::InitializeModApi(lua_State *L, int top) { LuaItemStack::Register(L); + LuaPerlinNoise::Register(L); + LuaPerlinNoiseMap::Register(L); + LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); + LuaSecureRandom::Register(L); ItemStackMetaRef::Register(L); LuaRaycast::Register(L); StorageRef::Register(L); @@ -73,8 +86,14 @@ 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); + LuaVoxelManip::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/server/serverinventorymgr.h b/src/server/serverinventorymgr.h index 0e4b72415..b6541bd3c 100644 --- a/src/server/serverinventorymgr.h +++ b/src/server/serverinventorymgr.h @@ -43,7 +43,8 @@ public: Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, const std::string &player = ""); bool removeDetachedInventory(const std::string &name); - bool checkDetachedInventoryAccess(const InventoryLocation &loc, const std::string &player) const; + bool checkDetachedInventoryAccess( + const InventoryLocation &loc, const std::string &player) const; void sendDetachedInventories(const std::string &peer_name, bool incremental, std::function<void(const std::string &, Inventory *)> apply_cb); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 8989fb05f..9da5f98bd 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1185,7 +1185,7 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode) // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - m_script->removeObjectReference(obj); + m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj)); // Delete active object if (obj->environmentDeletes()) @@ -1788,7 +1788,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, } // Register reference in scripting api (must be done before post-init) - m_script->addObjectReference(object); + m_script->addObjectReference(dynamic_cast<ActiveObject *>(object)); // Post-initialize object object->addedToEnvironment(dtime_s); @@ -1878,7 +1878,7 @@ void ServerEnvironment::removeRemovedObjects() // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - m_script->removeObjectReference(obj); + m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj)); // Delete if (obj->environmentDeletes()) @@ -2143,7 +2143,7 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - m_script->removeObjectReference(obj); + m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj)); // Delete active object if (obj->environmentDeletes()) diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index af30c209d..e5a4fac5b 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -48,7 +48,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; } @@ -81,8 +83,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; |