forked from oerkki/voxelands
add support for throwing objects, add snowball mob
This commit is contained in:
parent
0523c7c905
commit
0460fa439b
|
@ -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<u8> data((u8*)s.c_str(), s.size());
|
||||||
|
// Send as reliable
|
||||||
|
Send(0, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
|
void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
|
||||||
{
|
{
|
||||||
if (connectedAndInitialized() == false) {
|
if (connectedAndInitialized() == false) {
|
||||||
|
|
|
@ -207,6 +207,7 @@ public:
|
||||||
|
|
||||||
void groundAction(u8 action, v3s16 nodepos_undersurface,
|
void groundAction(u8 action, v3s16 nodepos_undersurface,
|
||||||
v3s16 nodepos_oversurface, u16 item);
|
v3s16 nodepos_oversurface, u16 item);
|
||||||
|
void throwItem(v3f dir, u16 item);
|
||||||
void clickActiveObject(u8 button, u16 id, u16 item_i);
|
void clickActiveObject(u8 button, u16 id, u16 item_i);
|
||||||
|
|
||||||
void sendNodemetaFields(v3s16 p, const std::string &formname,
|
void sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||||
|
|
|
@ -251,6 +251,14 @@ enum ToServerCommand
|
||||||
[0] u16 TOSERVER_INIT2
|
[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_GETBLOCK=0x20, // Obsolete
|
||||||
TOSERVER_ADDNODE = 0x21, // Obsolete
|
TOSERVER_ADDNODE = 0x21, // Obsolete
|
||||||
TOSERVER_REMOVENODE = 0x22, // Obsolete
|
TOSERVER_REMOVENODE = 0x22, // Obsolete
|
||||||
|
|
|
@ -375,6 +375,7 @@ void content_craftitem_init()
|
||||||
f->texture = "snow_ball.png";
|
f->texture = "snow_ball.png";
|
||||||
f->name = "snow_ball";
|
f->name = "snow_ball";
|
||||||
f->description = wgettext("Snow Ball");
|
f->description = wgettext("Snow Ball");
|
||||||
|
f->thrown_item = CONTENT_MOB_SNOWBALL;
|
||||||
|
|
||||||
i = CONTENT_CRAFTITEM_STICK;
|
i = CONTENT_CRAFTITEM_STICK;
|
||||||
f = &g_content_craftitem_features[i];
|
f = &g_content_craftitem_features[i];
|
||||||
|
|
|
@ -44,6 +44,8 @@ struct CraftItemFeatures {
|
||||||
s16 drop_count;
|
s16 drop_count;
|
||||||
// used by mobs that are picked up
|
// used by mobs that are picked up
|
||||||
content_t drop_item;
|
content_t drop_item;
|
||||||
|
// used by snowballs and such... things that are thrown
|
||||||
|
content_t thrown_item;
|
||||||
|
|
||||||
CraftItemFeatures():
|
CraftItemFeatures():
|
||||||
content(CONTENT_IGNORE),
|
content(CONTENT_IGNORE),
|
||||||
|
@ -54,7 +56,8 @@ struct CraftItemFeatures {
|
||||||
fuel_time(0.0),
|
fuel_time(0.0),
|
||||||
edible(0),
|
edible(0),
|
||||||
drop_count(-1),
|
drop_count(-1),
|
||||||
drop_item(CONTENT_IGNORE)
|
drop_item(CONTENT_IGNORE),
|
||||||
|
thrown_item(CONTENT_IGNORE)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -513,4 +513,21 @@ void content_mob_init()
|
||||||
f->spawn_max_nearby_mobs = 3;
|
f->spawn_max_nearby_mobs = 3;
|
||||||
f->lifetime = 900.0;
|
f->lifetime = 900.0;
|
||||||
f->setCollisionBox(aabb3f(-0.4*BS, 0., -0.4*BS, 0.4*BS, 1.*BS, 0.4*BS));
|
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.));
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct MobFeatures {
|
||||||
MobMotion motion;
|
MobMotion motion;
|
||||||
MobMotionType motion_type;
|
MobMotionType motion_type;
|
||||||
MobMotion angry_motion;
|
MobMotion angry_motion;
|
||||||
|
f32 static_thrown_speed;
|
||||||
content_t hunted_node;
|
content_t hunted_node;
|
||||||
content_t fleed_node;
|
content_t fleed_node;
|
||||||
bool notices_player;
|
bool notices_player;
|
||||||
|
@ -123,6 +124,8 @@ struct MobFeatures {
|
||||||
u16 special_dropped_max;
|
u16 special_dropped_max;
|
||||||
f32 lifetime;
|
f32 lifetime;
|
||||||
u16 contact_explosion_diameter;
|
u16 contact_explosion_diameter;
|
||||||
|
content_t contact_place_node;
|
||||||
|
content_t contact_drop_item;
|
||||||
|
|
||||||
std::string sound_death;
|
std::string sound_death;
|
||||||
std::string sound_attack;
|
std::string sound_attack;
|
||||||
|
@ -225,6 +228,7 @@ struct MobFeatures {
|
||||||
motion = MM_STATIC;
|
motion = MM_STATIC;
|
||||||
motion_type = MMT_WALK;
|
motion_type = MMT_WALK;
|
||||||
angry_motion = MM_STATIC;
|
angry_motion = MM_STATIC;
|
||||||
|
static_thrown_speed = 20.0;
|
||||||
hunted_node = CONTENT_IGNORE;
|
hunted_node = CONTENT_IGNORE;
|
||||||
fleed_node = CONTENT_IGNORE;
|
fleed_node = CONTENT_IGNORE;
|
||||||
notices_player = false;
|
notices_player = false;
|
||||||
|
@ -243,6 +247,8 @@ struct MobFeatures {
|
||||||
special_dropped_max = 0;
|
special_dropped_max = 0;
|
||||||
lifetime = 0.0;
|
lifetime = 0.0;
|
||||||
contact_explosion_diameter = 0;
|
contact_explosion_diameter = 0;
|
||||||
|
contact_place_node = CONTENT_IGNORE;
|
||||||
|
contact_drop_item = CONTENT_IGNORE;
|
||||||
sound_death = "";
|
sound_death = "";
|
||||||
sound_attack = "";
|
sound_attack = "";
|
||||||
sound_punch = "mob-dig";
|
sound_punch = "mob-dig";
|
||||||
|
@ -300,5 +306,6 @@ void content_mob_init();
|
||||||
#define CONTENT_MOB_WOLF (CONTENT_MOB_MASK | 0x0B)
|
#define CONTENT_MOB_WOLF (CONTENT_MOB_MASK | 0x0B)
|
||||||
#define CONTENT_MOB_TAMEWOLF (CONTENT_MOB_MASK | 0x0C)
|
#define CONTENT_MOB_TAMEWOLF (CONTENT_MOB_MASK | 0x0C)
|
||||||
#define CONTENT_MOB_SHEEP (CONTENT_MOB_MASK | 0x0D)
|
#define CONTENT_MOB_SHEEP (CONTENT_MOB_MASK | 0x0D)
|
||||||
|
#define CONTENT_MOB_SNOWBALL (CONTENT_MOB_MASK | 0x0E)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1091,12 +1091,24 @@ void MobSAO::stepMotionThrown(float dtime)
|
||||||
{
|
{
|
||||||
MobFeatures m = content_mob_features(m_content);
|
MobFeatures m = content_mob_features(m_content);
|
||||||
m_base_position += m_speed * dtime;
|
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);
|
v3s16 pos_i = floatToInt(m_base_position, BS);
|
||||||
if (!checkFreePosition(pos_i)) {
|
if (!checkFreePosition(pos_i)) {
|
||||||
if (m.contact_explosion_diameter > 0)
|
if (m.contact_explosion_diameter > 0)
|
||||||
explodeSquare(pos_i, v3s16(m.contact_explosion_diameter,m.contact_explosion_diameter,m.contact_explosion_diameter));
|
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;
|
m_removed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
53
src/game.cpp
53
src/game.cpp
|
@ -1621,7 +1621,7 @@ void the_game(
|
||||||
|
|
||||||
{
|
{
|
||||||
// Read client events
|
// Read client events
|
||||||
for (;;) {
|
while (1) {
|
||||||
ClientEvent event = client.getClientEvent();
|
ClientEvent event = client.getClientEvent();
|
||||||
if (event.type == CE_NONE) {
|
if (event.type == CE_NONE) {
|
||||||
break;
|
break;
|
||||||
|
@ -1666,8 +1666,7 @@ void the_game(
|
||||||
//bool camera_offset_changed = (camera_offset != old_camera_offset);
|
//bool camera_offset_changed = (camera_offset != old_camera_offset);
|
||||||
|
|
||||||
if (!disable_camera_update) {
|
if (!disable_camera_update) {
|
||||||
client.updateCamera(camera_position,
|
client.updateCamera(camera_position, camera_direction, camera_fov, camera_offset);
|
||||||
camera_direction, camera_fov, camera_offset);
|
|
||||||
client.updateCameraOffset(camera_offset);
|
client.updateCameraOffset(camera_offset);
|
||||||
client.getEnv().updateObjectsCameraOffset(camera_offset);
|
client.getEnv().updateObjectsCameraOffset(camera_offset);
|
||||||
update_particles_camera_offset(camera_offset);
|
update_particles_camera_offset(camera_offset);
|
||||||
|
@ -1675,20 +1674,30 @@ void the_game(
|
||||||
clouds->updateCameraOffset(camera_offset);
|
clouds->updateCameraOffset(camera_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool left_punch = false;
|
||||||
|
bool left_punch_muted = false;
|
||||||
|
|
||||||
|
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{
|
||||||
/*
|
/*
|
||||||
Calculate what block is the crosshair pointing to
|
Calculate what block is the crosshair pointing to
|
||||||
*/
|
*/
|
||||||
|
|
||||||
f32 d = 4; // max. distance
|
f32 d = 4; // max. distance
|
||||||
core::line3d<f32> shootline(camera_position,
|
core::line3d<f32> shootline(camera_position, camera_position + camera_direction * BS * (d+1));
|
||||||
camera_position + camera_direction * BS * (d+1));
|
|
||||||
|
|
||||||
ClientActiveObject *selected_active_object
|
ClientActiveObject *selected_active_object = client.getSelectedActiveObject(d*BS, camera_position, shootline);
|
||||||
= client.getSelectedActiveObject
|
|
||||||
(d*BS, camera_position, shootline);
|
|
||||||
|
|
||||||
bool left_punch = false;
|
|
||||||
bool left_punch_muted = false;
|
|
||||||
|
|
||||||
if (selected_active_object != NULL) {
|
if (selected_active_object != NULL) {
|
||||||
client.setPointedContent(selected_active_object->getContent());
|
client.setPointedContent(selected_active_object->getContent());
|
||||||
|
@ -1717,8 +1726,7 @@ void the_game(
|
||||||
|
|
||||||
infotext = narrow_to_wide(selected_active_object->infoText());
|
infotext = narrow_to_wide(selected_active_object->infoText());
|
||||||
|
|
||||||
if(input->getLeftState())
|
if (input->getLeftState()) {
|
||||||
{
|
|
||||||
bool do_punch = false;
|
bool do_punch = false;
|
||||||
bool do_punch_damage = false;
|
bool do_punch_damage = false;
|
||||||
if (object_hit_delay_timer <= 0.0){
|
if (object_hit_delay_timer <= 0.0){
|
||||||
|
@ -1734,20 +1742,13 @@ void the_game(
|
||||||
left_punch = true;
|
left_punch = true;
|
||||||
}
|
}
|
||||||
if (do_punch_damage) {
|
if (do_punch_damage) {
|
||||||
client.clickActiveObject(0,
|
client.clickActiveObject(0, selected_active_object->getId(), g_selected_item);
|
||||||
selected_active_object->getId(), g_selected_item);
|
|
||||||
}
|
}
|
||||||
}
|
}else if (input->getRightClicked()) {
|
||||||
else if(input->getRightClicked())
|
|
||||||
{
|
|
||||||
infostream<<"Right-clicked object"<<std::endl;
|
infostream<<"Right-clicked object"<<std::endl;
|
||||||
client.clickActiveObject(1,
|
client.clickActiveObject(1, selected_active_object->getId(), g_selected_item);
|
||||||
selected_active_object->getId(), g_selected_item);
|
|
||||||
}
|
}
|
||||||
}
|
}else{ // selected_object == NULL
|
||||||
else // selected_object == NULL
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Find out which node we are pointing at
|
Find out which node we are pointing at
|
||||||
*/
|
*/
|
||||||
|
@ -1917,6 +1918,7 @@ void the_game(
|
||||||
}
|
}
|
||||||
|
|
||||||
} // selected_object == NULL
|
} // selected_object == NULL
|
||||||
|
}
|
||||||
|
|
||||||
if (left_punch || (input->getLeftClicked() && !left_punch_muted))
|
if (left_punch || (input->getLeftClicked() && !left_punch_muted))
|
||||||
camera.setDigging(0); // left click animation
|
camera.setDigging(0); // left click animation
|
||||||
|
@ -2026,8 +2028,7 @@ void the_game(
|
||||||
/*
|
/*
|
||||||
Update gui stuff (0ms)
|
Update gui stuff (0ms)
|
||||||
*/
|
*/
|
||||||
const char program_name_and_version[] =
|
const char program_name_and_version[] = "Voxelands " VERSION_STRING;
|
||||||
"Voxelands " VERSION_STRING;
|
|
||||||
if (show_debug) {
|
if (show_debug) {
|
||||||
static float drawtime_avg = 0;
|
static float drawtime_avg = 0;
|
||||||
drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
|
drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "content_nodemeta.h"
|
#include "content_nodemeta.h"
|
||||||
#include "mapblock.h"
|
#include "mapblock.h"
|
||||||
#include "serverobject.h"
|
#include "serverobject.h"
|
||||||
|
#include "content_sao.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -2102,6 +2103,74 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (command) {
|
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"<<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
v3f pf((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
|
||||||
|
v3f sf((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
|
||||||
|
|
||||||
|
ServerActiveObject *obj = new MobSAO(&m_env, 0, pf, sf*content_mob_features(thrown).static_thrown_speed*BS, thrown);
|
||||||
|
|
||||||
|
if (obj == NULL) {
|
||||||
|
infostream<<"WARNING: item resulted in NULL object, "
|
||||||
|
<<"not throwing into map"
|
||||||
|
<<std::endl;
|
||||||
|
}else{
|
||||||
|
actionstream<<player->getName()<<" throws "<<thrown<<" at "<<PP(pf/BS)<<" ("<<PP(sf/BS)")"<<std::endl;
|
||||||
|
|
||||||
|
// Add the object to the environment
|
||||||
|
m_env.addActiveObject(obj);
|
||||||
|
}
|
||||||
|
if (g_settings->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"<<std::endl;
|
||||||
|
|
||||||
|
InventoryList *ilist = player->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:
|
case TOSERVER_PLAYERPOS:
|
||||||
{
|
{
|
||||||
if (datasize < 2+12+12+4+4)
|
if (datasize < 2+12+12+4+4)
|
||||||
|
|
Loading…
Reference in New Issue