From 0460fa439b00e296a0cedece206dae22369f9d7a Mon Sep 17 00:00:00 2001 From: darkrose Date: Sat, 22 Nov 2014 01:31:18 +1000 Subject: [PATCH] add support for throwing objects, add snowball mob --- src/client.cpp | 33 +++ src/client.h | 1 + src/clientserver.h | 8 + src/content_craftitem.cpp | 1 + src/content_craftitem.h | 5 +- src/content_mob.cpp | 17 ++ src/content_mob.h | 7 + src/content_sao.cpp | 14 +- src/game.cpp | 455 +++++++++++++++++++------------------- src/server.cpp | 71 +++++- 10 files changed, 382 insertions(+), 230 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 10e80b6..3b93518 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1591,6 +1591,39 @@ void Client::groundAction(u8 action, v3s16 nodepos_undersurface, } } +void Client::throwItem(v3f dir, u16 item) +{ + std::ostringstream os(std::ios_base::binary); + u8 buf[15]; + + LocalPlayer* player = m_env.getLocalPlayer(); + + // Write command + writeU16(buf, TOSERVER_THROWITEM); + os.write((char*)buf, 2); + + // Write position + v3f pf = player->getEyePosition(); + v3s32 position(pf.X*100, pf.Y*100, pf.Z*100); + writeV3S32(buf,position); + os.write((char*)buf, 12); + + // Write speed/direction + v3s32 dir_i(dir.X*100, dir.Y*100, dir.Z*100); + writeV3S32(buf,dir_i); + os.write((char*)buf, 12); + + // Write item + writeU16(buf, item); + os.write((char*)buf, 2); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::clickActiveObject(u8 button, u16 id, u16 item_i) { if (connectedAndInitialized() == false) { diff --git a/src/client.h b/src/client.h index de822a7..2d7a2b4 100644 --- a/src/client.h +++ b/src/client.h @@ -207,6 +207,7 @@ public: void groundAction(u8 action, v3s16 nodepos_undersurface, v3s16 nodepos_oversurface, u16 item); + void throwItem(v3f dir, u16 item); void clickActiveObject(u8 button, u16 id, u16 item_i); void sendNodemetaFields(v3s16 p, const std::string &formname, diff --git a/src/clientserver.h b/src/clientserver.h index ba7ffab..794d557 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -251,6 +251,14 @@ enum ToServerCommand [0] u16 TOSERVER_INIT2 */ + TOSERVER_THROWITEM = 0x12, + /* + [0] u16 command + [2] v3s32 position*100 + [14] v3s32 speed*100 + [26] u16 item + */ + TOSERVER_GETBLOCK=0x20, // Obsolete TOSERVER_ADDNODE = 0x21, // Obsolete TOSERVER_REMOVENODE = 0x22, // Obsolete diff --git a/src/content_craftitem.cpp b/src/content_craftitem.cpp index f9aaf9d..bd17fcd 100644 --- a/src/content_craftitem.cpp +++ b/src/content_craftitem.cpp @@ -375,6 +375,7 @@ void content_craftitem_init() f->texture = "snow_ball.png"; f->name = "snow_ball"; f->description = wgettext("Snow Ball"); + f->thrown_item = CONTENT_MOB_SNOWBALL; i = CONTENT_CRAFTITEM_STICK; f = &g_content_craftitem_features[i]; diff --git a/src/content_craftitem.h b/src/content_craftitem.h index 665c3d8..07e9de0 100644 --- a/src/content_craftitem.h +++ b/src/content_craftitem.h @@ -44,6 +44,8 @@ struct CraftItemFeatures { s16 drop_count; // used by mobs that are picked up content_t drop_item; + // used by snowballs and such... things that are thrown + content_t thrown_item; CraftItemFeatures(): content(CONTENT_IGNORE), @@ -54,7 +56,8 @@ struct CraftItemFeatures { fuel_time(0.0), edible(0), drop_count(-1), - drop_item(CONTENT_IGNORE) + drop_item(CONTENT_IGNORE), + thrown_item(CONTENT_IGNORE) {} }; diff --git a/src/content_mob.cpp b/src/content_mob.cpp index 7f37d44..cc45de2 100644 --- a/src/content_mob.cpp +++ b/src/content_mob.cpp @@ -513,4 +513,21 @@ void content_mob_init() f->spawn_max_nearby_mobs = 3; f->lifetime = 900.0; f->setCollisionBox(aabb3f(-0.4*BS, 0., -0.4*BS, 0.4*BS, 1.*BS, 0.4*BS)); + + i = CONTENT_MOB_SNOWBALL; + f = &g_content_mob_features[i]; + f->content = i; + f->level = MOB_AGGRESSIVE; + f->setTexture("snow_ball.png"); + f->model_offset = v3f(0,0.2,0); + f->punch_action = MPA_IGNORE; + f->motion = MM_THROWN; + f->motion_type = MMT_FLY; + f->notices_player = true; + f->attack_player_damage = 1; + f->attack_player_range = v3f(1,1,1); + f->lifetime = 10.0; + f->contact_place_node = CONTENT_SNOW; + f->contact_drop_item = CONTENT_CRAFTITEM_SNOW_BALL; + f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.)); } diff --git a/src/content_mob.h b/src/content_mob.h index f6aa3ab..6875477 100644 --- a/src/content_mob.h +++ b/src/content_mob.h @@ -105,6 +105,7 @@ struct MobFeatures { MobMotion motion; MobMotionType motion_type; MobMotion angry_motion; + f32 static_thrown_speed; content_t hunted_node; content_t fleed_node; bool notices_player; @@ -123,6 +124,8 @@ struct MobFeatures { u16 special_dropped_max; f32 lifetime; u16 contact_explosion_diameter; + content_t contact_place_node; + content_t contact_drop_item; std::string sound_death; std::string sound_attack; @@ -225,6 +228,7 @@ struct MobFeatures { motion = MM_STATIC; motion_type = MMT_WALK; angry_motion = MM_STATIC; + static_thrown_speed = 20.0; hunted_node = CONTENT_IGNORE; fleed_node = CONTENT_IGNORE; notices_player = false; @@ -243,6 +247,8 @@ struct MobFeatures { special_dropped_max = 0; lifetime = 0.0; contact_explosion_diameter = 0; + contact_place_node = CONTENT_IGNORE; + contact_drop_item = CONTENT_IGNORE; sound_death = ""; sound_attack = ""; sound_punch = "mob-dig"; @@ -300,5 +306,6 @@ void content_mob_init(); #define CONTENT_MOB_WOLF (CONTENT_MOB_MASK | 0x0B) #define CONTENT_MOB_TAMEWOLF (CONTENT_MOB_MASK | 0x0C) #define CONTENT_MOB_SHEEP (CONTENT_MOB_MASK | 0x0D) +#define CONTENT_MOB_SNOWBALL (CONTENT_MOB_MASK | 0x0E) #endif diff --git a/src/content_sao.cpp b/src/content_sao.cpp index e79d3d8..d002ccb 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1091,12 +1091,24 @@ void MobSAO::stepMotionThrown(float dtime) { MobFeatures m = content_mob_features(m_content); m_base_position += m_speed * dtime; - m_base_position.Y -= 0.1*BS*dtime; + m_speed.Y -= 10.0*BS*dtime; v3s16 pos_i = floatToInt(m_base_position, BS); if (!checkFreePosition(pos_i)) { if (m.contact_explosion_diameter > 0) explodeSquare(pos_i, v3s16(m.contact_explosion_diameter,m.contact_explosion_diameter,m.contact_explosion_diameter)); + if (m.contact_place_node != CONTENT_IGNORE && checkFreeAndWalkablePosition(pos_i+v3s16(0,1,0))) { + v3s16 pos = pos_i+v3s16(0,1,0); + m_env->getMap().addNodeWithEvent(pos,MapNode(m.contact_place_node)); + }else if (m.contact_drop_item != CONTENT_IGNORE) { + v3f pos = intToFloat(pos_i+v3s16(0,1,0),BS); + InventoryItem *i = InventoryItem::create(m.contact_drop_item,1); + if (i) { + ServerActiveObject *obj = i->createSAO(m_env,0,m_base_position); + if (obj) + m_env->addActiveObject(obj); + } + } m_removed = true; return; } diff --git a/src/game.cpp b/src/game.cpp index dd515e3..2e46e9d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1621,7 +1621,7 @@ void the_game( { // Read client events - for (;;) { + while (1) { ClientEvent event = client.getClientEvent(); if (event.type == CE_NONE) { break; @@ -1665,9 +1665,8 @@ void the_game( //bool camera_offset_changed = (camera_offset != old_camera_offset); - if(!disable_camera_update){ - client.updateCamera(camera_position, - camera_direction, camera_fov, camera_offset); + if (!disable_camera_update) { + client.updateCamera(camera_position, camera_direction, camera_fov, camera_offset); client.updateCameraOffset(camera_offset); client.getEnv().updateObjectsCameraOffset(camera_offset); update_particles_camera_offset(camera_offset); @@ -1675,249 +1674,252 @@ void the_game( clouds->updateCameraOffset(camera_offset); } - /* - Calculate what block is the crosshair pointing to - */ - - f32 d = 4; // max. distance - core::line3d shootline(camera_position, - camera_position + camera_direction * BS * (d+1)); - - ClientActiveObject *selected_active_object - = client.getSelectedActiveObject - (d*BS, camera_position, shootline); - bool left_punch = false; bool left_punch_muted = false; - if (selected_active_object != NULL) { - client.setPointedContent(selected_active_object->getContent()); - /* Clear possible cracking animation */ - if (nodepos_old != v3s16(-32768,-32768,-32768)) { - client.clearTempMod(nodepos_old); - dig_time = 0.0; - nodepos_old = v3s16(-32768,-32768,-32768); - } - - core::aabbox3d *selection_box - = selected_active_object->getSelectionBox(); - // Box should exist because object was returned in the - // first place - assert(selection_box); - - v3f pos = selected_active_object->getPosition()-intToFloat(camera_offset,BS); - - core::aabbox3d box_on_map( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); - - if(selected_active_object->doShowSelectionBox()) - hilightboxes.push_back(box_on_map); - - infotext = narrow_to_wide(selected_active_object->infoText()); - - if(input->getLeftState()) - { - bool do_punch = false; - bool do_punch_damage = false; - if(object_hit_delay_timer <= 0.0){ - do_punch = true; - do_punch_damage = true; - object_hit_delay_timer = object_hit_delay; - } - if(input->getLeftClicked()){ - do_punch = true; - } - if(do_punch){ - infostream<<"Left-clicked object"<getId(), g_selected_item); - } - } - else if(input->getRightClicked()) - { - infostream<<"Right-clicked object"<getId(), g_selected_item); - } - } - else // selected_object == NULL - { - - /* - Find out which node we are pointing at - */ - - bool nodefound = false; - v3s16 nodepos; - v3s16 neighbourpos; - core::aabbox3d nodehilightbox; - - getPointedNode(&client, player_position, - camera_direction, camera_position, - nodefound, shootline, - nodepos, neighbourpos, camera_offset, - nodehilightbox, d); - - if (!nodefound) { - if (nodepos_old != v3s16(-32768,-32768,-32768)) { - client.clearTempMod(nodepos_old); - dig_time = 0.0; - nodepos_old = v3s16(-32768,-32768,-32768); - } + InventoryItem *wield = (InventoryItem*)client.getLocalPlayer()->getWieldItem(); + if ( + wield + && ( + content_craftitem_features(wield->getContent()).thrown_item != CONTENT_IGNORE + //|| ( + //content_toolitem_features(wield->getContent()).thrown_item != CONTENT_IGNORE + //&& client.getLocalPlayer()->inventory.find(content_toolitem_features(wield->getContent()).thrown_item) > -1 + //) + ) && input->getLeftClicked() + ) { + client.throwItem(camera_direction,g_selected_item); }else{ - /* - Check information text of node + Calculate what block is the crosshair pointing to */ - if (nodepos != nodepos_old && nodepos_old != v3s16(-32768,-32768,-32768)) - client.clearTempMod(nodepos_old); - NodeMetadata *meta = client.getNodeMetadata(nodepos); - if (meta) - infotext = meta->infoText(); + f32 d = 4; // max. distance + core::line3d shootline(camera_position, camera_position + camera_direction * BS * (d+1)); - /* - Handle digging - */ + ClientActiveObject *selected_active_object = client.getSelectedActiveObject(d*BS, camera_position, shootline); - if (input->getLeftReleased()) { - client.clearTempMod(nodepos); - dig_time = 0.0; - } - /* - Visualize selection - */ + if (selected_active_object != NULL) { + client.setPointedContent(selected_active_object->getContent()); + /* Clear possible cracking animation */ + if (nodepos_old != v3s16(-32768,-32768,-32768)) { + client.clearTempMod(nodepos_old); + dig_time = 0.0; + nodepos_old = v3s16(-32768,-32768,-32768); + } - if (g_settings->exists("selected_node") && g_settings->get("selected_node") == "outline") { - hilightboxes.push_back(nodehilightbox); - }else{ - client.setTempMod(nodepos, NodeMod(NODEMOD_SELECTION)); - } + core::aabbox3d *selection_box + = selected_active_object->getSelectionBox(); + // Box should exist because object was returned in the + // first place + assert(selection_box); - if (nodig_delay_counter > 0.0) { - nodig_delay_counter -= dtime; - }else{ - if (nodepos != nodepos_old) { - infostream<<"Pointing at ("<getPosition()-intToFloat(camera_offset,BS); + core::aabbox3d box_on_map( + selection_box->MinEdge + pos, + selection_box->MaxEdge + pos + ); + + if (selected_active_object->doShowSelectionBox()) + hilightboxes.push_back(box_on_map); + + infotext = narrow_to_wide(selected_active_object->infoText()); + + if (input->getLeftState()) { + bool do_punch = false; + bool do_punch_damage = false; + if (object_hit_delay_timer <= 0.0){ + do_punch = true; + do_punch_damage = true; + object_hit_delay_timer = object_hit_delay; + } + if (input->getLeftClicked()) { + do_punch = true; + } + if (do_punch) { + infostream<<"Left-clicked object"<getId(), g_selected_item); + } + }else if (input->getRightClicked()) { + infostream<<"Right-clicked object"<getId(), g_selected_item); + } + }else{ // selected_object == NULL + /* + Find out which node we are pointing at + */ + + bool nodefound = false; + v3s16 nodepos; + v3s16 neighbourpos; + core::aabbox3d nodehilightbox; + + getPointedNode(&client, player_position, + camera_direction, camera_position, + nodefound, shootline, + nodepos, neighbourpos, camera_offset, + nodehilightbox, d); + + if (!nodefound) { if (nodepos_old != v3s16(-32768,-32768,-32768)) { client.clearTempMod(nodepos_old); dig_time = 0.0; nodepos_old = v3s16(-32768,-32768,-32768); } - } - - if (input->getLeftClicked() || (input->getLeftState() && nodepos != nodepos_old)) { - infostream<<"Started digging"<getLeftClicked()) - client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0)); - if (input->getLeftState()) { - MapNode n = client.getNode(nodepos); - - // Get tool name. Default is "" = bare hands - content_t toolid = CONTENT_IGNORE; - InventoryList *mlist = local_inventory.getList("main"); - if (mlist != NULL) { - InventoryItem *item = mlist->getItem(g_selected_item); - if (item && (std::string)item->getName() == "ToolItem") { - ToolItem *titem = (ToolItem*)item; - toolid = titem->getContent(); - } - } - - // Get digging properties for material and tool - content_t material = n.getContent(); - DiggingProperties prop = getDiggingProperties(material, toolid); - - float dig_time_complete = 0.0; - - if (prop.diggable == false) { - dig_time_complete = 10000000.0; - client.clearTempMod(nodepos); - }else{ - dig_time_complete = prop.time; - if (g_settings->getBool("enable_particles")) - addPunchingParticles(smgr, player, nodepos, content_features(n).tiles); - - if (dig_time_complete >= 0.001) { - dig_index = (u16)((float)CRACK_ANIMATION_LENGTH - * dig_time/dig_time_complete); - }else { - // This is for torches - dig_index = CRACK_ANIMATION_LENGTH; - } - - if (dig_index < CRACK_ANIMATION_LENGTH) { - client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index)); - }else{ - infostream<<"Digging completed"<getBool("enable_particles")) - addDiggingParticles(smgr, player, nodepos, content_features(n).tiles); - - dig_time = 0; - - nodig_delay_counter = dig_time_complete - / (float)CRACK_ANIMATION_LENGTH; - - // We don't want a corresponding delay to - // very time consuming nodes - if (nodig_delay_counter > 0.5) - nodig_delay_counter = 0.5; - // We want a slight delay to very little - // time consuming nodes - float mindelay = 0.15; - if (nodig_delay_counter < mindelay) - nodig_delay_counter = mindelay; - } - } - - dig_time += dtime; - - camera.setDigging(0); // left click animation - } - } - - - if (input->getRightClicked()) { - infostream<<"Ground right-clicked"<getDrawSpecString() != "" && !random_input) { - infostream<<"Launching custom inventory view"<setFormSpec(meta->getDrawSpecString(), inventoryloc); - menu->setFormIO(new NodeMetadataFormIO(nodepos, &client)); - menu->drop(); - }else{ - client.groundAction(1, nodepos, neighbourpos, g_selected_item); - camera.setDigging(1); // right click animation + + /* + Check information text of node + */ + + if (nodepos != nodepos_old && nodepos_old != v3s16(-32768,-32768,-32768)) + client.clearTempMod(nodepos_old); + NodeMetadata *meta = client.getNodeMetadata(nodepos); + if (meta) + infotext = meta->infoText(); + + /* + Handle digging + */ + + if (input->getLeftReleased()) { + client.clearTempMod(nodepos); + dig_time = 0.0; + } + /* + Visualize selection + */ + + if (g_settings->exists("selected_node") && g_settings->get("selected_node") == "outline") { + hilightboxes.push_back(nodehilightbox); + }else{ + client.setTempMod(nodepos, NodeMod(NODEMOD_SELECTION)); + } + + if (nodig_delay_counter > 0.0) { + nodig_delay_counter -= dtime; + }else{ + if (nodepos != nodepos_old) { + infostream<<"Pointing at ("<getLeftClicked() || (input->getLeftState() && nodepos != nodepos_old)) { + infostream<<"Started digging"<getLeftClicked()) + client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0)); + if (input->getLeftState()) { + MapNode n = client.getNode(nodepos); + + // Get tool name. Default is "" = bare hands + content_t toolid = CONTENT_IGNORE; + InventoryList *mlist = local_inventory.getList("main"); + if (mlist != NULL) { + InventoryItem *item = mlist->getItem(g_selected_item); + if (item && (std::string)item->getName() == "ToolItem") { + ToolItem *titem = (ToolItem*)item; + toolid = titem->getContent(); + } + } + + // Get digging properties for material and tool + content_t material = n.getContent(); + DiggingProperties prop = getDiggingProperties(material, toolid); + + float dig_time_complete = 0.0; + + if (prop.diggable == false) { + dig_time_complete = 10000000.0; + client.clearTempMod(nodepos); + }else{ + dig_time_complete = prop.time; + if (g_settings->getBool("enable_particles")) + addPunchingParticles(smgr, player, nodepos, content_features(n).tiles); + + if (dig_time_complete >= 0.001) { + dig_index = (u16)((float)CRACK_ANIMATION_LENGTH + * dig_time/dig_time_complete); + }else { + // This is for torches + dig_index = CRACK_ANIMATION_LENGTH; + } + + if (dig_index < CRACK_ANIMATION_LENGTH) { + client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index)); + }else{ + infostream<<"Digging completed"<getBool("enable_particles")) + addDiggingParticles(smgr, player, nodepos, content_features(n).tiles); + + dig_time = 0; + + nodig_delay_counter = dig_time_complete + / (float)CRACK_ANIMATION_LENGTH; + + // We don't want a corresponding delay to + // very time consuming nodes + if (nodig_delay_counter > 0.5) + nodig_delay_counter = 0.5; + // We want a slight delay to very little + // time consuming nodes + float mindelay = 0.15; + if (nodig_delay_counter < mindelay) + nodig_delay_counter = mindelay; + } + } + + dig_time += dtime; + + camera.setDigging(0); // left click animation + } + } + + + if (input->getRightClicked()) { + infostream<<"Ground right-clicked"<getDrawSpecString() != "" && !random_input) { + infostream<<"Launching custom inventory view"<setFormSpec(meta->getDrawSpecString(), inventoryloc); + menu->setFormIO(new NodeMetadataFormIO(nodepos, &client)); + menu->drop(); + + }else{ + client.groundAction(1, nodepos, neighbourpos, g_selected_item); + camera.setDigging(1); // right click animation + } + } + + nodepos_old = nodepos; } - } - nodepos_old = nodepos; + } // selected_object == NULL } - } // selected_object == NULL - if (left_punch || (input->getLeftClicked() && !left_punch_muted)) camera.setDigging(0); // left click animation @@ -2026,8 +2028,7 @@ void the_game( /* Update gui stuff (0ms) */ - const char program_name_and_version[] = - "Voxelands " VERSION_STRING; + const char program_name_and_version[] = "Voxelands " VERSION_STRING; if (show_debug) { static float drawtime_avg = 0; drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05; diff --git a/src/server.cpp b/src/server.cpp index 67fa2d5..f590d0d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -44,6 +44,7 @@ #include "content_nodemeta.h" #include "mapblock.h" #include "serverobject.h" +#include "content_sao.h" #include "settings.h" #include "profiler.h" #include "log.h" @@ -2102,9 +2103,77 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } switch (command) { + case TOSERVER_THROWITEM: + { + if (datasize < 2+12+12+2) + return; + v3s32 ps = readV3S32(&data[2]); + v3s32 ss = readV3S32(&data[2+12]); + + u16 item_i = readU16(&data[2+12+12]); + + InventoryList *ilist = player->inventory.getList("main"); + if (ilist == NULL) + return; + + // Get item + InventoryItem *item = ilist->getItem(item_i); + + // If there is no item, it is not possible to throw it + if (item == NULL) + return; + + content_t thrown = content_craftitem_features(item->getContent()).thrown_item; + // We can throw it, right? + if (thrown == CONTENT_IGNORE) + return; + + if (g_settings->getBool("droppable_inventory") == false || (getPlayerPrivs(player) & PRIV_BUILD) == 0) { + infostream<<"Not allowing player to drop item: creative mode and no build privs"<getName()<<" throws "<getBool("infinite_inventory") == false) { + // Delete the right amount of items from the slot + + // Delete item if all gone + if (item->getCount() <= 1) { + if (item->getCount() < 1) + infostream<<"WARNING: Server: dropped more items" + <<" than the slot contains"<inventory.getList("main"); + if (ilist) + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + }else{ + item->remove(1); + } + + // Send inventory + UpdateCrafting(peer_id); + SendInventory(peer_id); + } + } + break; case TOSERVER_PLAYERPOS: { - if(datasize < 2+12+12+4+4) + if (datasize < 2+12+12+4+4) return; u32 start = 0;