blender/source/gameengine/GameLogic/SCA_ISensor.cpp
Jorge Bernal 8c98b1649d BGE: Fix for T42341 Sensor.frequency is badly named
"Frequency" parameter is renamed to "Skip" in the LogicBricks sensors as it represents skipped frames between pulses.

Naming something (frequency) the exact opposite of what it represents (period) was the worst choice.

Also, a new BGE python attribute 'skippedTicks' was introduced. 'frequency' attribute is maintained but deprecated.

Internally, freq variable is used yet at DNA_Sensor to maintain compability and to avoid do_versions.

Thanks to Sybren for the investigation.

{F162440}

Reviewers: campbellbarton, sybren, moguri, hg1

Reviewed By: sybren, hg1

Differential Revision: https://developer.blender.org/D1229
2015-04-16 06:39:33 +02:00

472 lines
12 KiB
C++

/*
* Abstract class for sensor logic bricks
*
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file gameengine/GameLogic/SCA_ISensor.cpp
* \ingroup gamelogic
*/
#include <stddef.h>
#include "SCA_ISensor.h"
#include "SCA_EventManager.h"
#include "SCA_LogicManager.h"
// needed for IsTriggered()
#include "SCA_PythonController.h"
#include <stdio.h>
/* Native functions */
void SCA_ISensor::ReParent(SCA_IObject* parent)
{
SCA_ILogicBrick::ReParent(parent);
// will be done when the sensor is activated
//m_eventmgr->RegisterSensor(this);
//this->SetActive(false);
}
SCA_ISensor::SCA_ISensor(SCA_IObject* gameobj,
class SCA_EventManager* eventmgr) :
SCA_ILogicBrick(gameobj)
{
m_links = 0;
m_suspended = false;
m_invert = false;
m_level = false;
m_tap = false;
m_reset = false;
m_pos_ticks = 0;
m_neg_ticks = 0;
m_pos_pulsemode = false;
m_neg_pulsemode = false;
m_skipped_ticks = 0;
m_state = false;
m_prev_state = false;
m_eventmgr = eventmgr;
}
SCA_ISensor::~SCA_ISensor()
{
// intentionally empty
}
void SCA_ISensor::ProcessReplica()
{
SCA_ILogicBrick::ProcessReplica();
m_linkedcontrollers.clear();
}
bool SCA_ISensor::IsPositiveTrigger()
{
bool result = false;
if (m_eventval) {
result = (m_eventval->GetNumber() != 0.0);
}
if (m_invert) {
result = !result;
}
return result;
}
void SCA_ISensor::SetPulseMode(bool posmode,
bool negmode,
int skippedticks)
{
m_pos_pulsemode = posmode;
m_neg_pulsemode = negmode;
m_skipped_ticks = skippedticks;
}
void SCA_ISensor::SetInvert(bool inv)
{
m_invert = inv;
}
void SCA_ISensor::SetLevel(bool lvl)
{
m_level = lvl;
}
void SCA_ISensor::SetTap(bool tap)
{
m_tap = tap;
}
double SCA_ISensor::GetNumber()
{
return GetState();
}
void SCA_ISensor::Suspend()
{
m_suspended = true;
}
bool SCA_ISensor::IsSuspended()
{
return m_suspended;
}
void SCA_ISensor::Resume()
{
m_suspended = false;
}
void SCA_ISensor::Init()
{
printf("Sensor %s has no init function, please report this bug to Blender.org\n", m_name.Ptr());
}
void SCA_ISensor::DecLink()
{
m_links--;
if (m_links < 0)
{
printf("Warning: sensor %s has negative m_links: %d\n", m_name.Ptr(), m_links);
m_links = 0;
}
if (!m_links)
{
// sensor is detached from all controllers, remove it from manager
UnregisterToManager();
}
}
void SCA_ISensor::RegisterToManager()
{
// sensor is just activated, initialize it
Init();
m_state = false;
m_eventmgr->RegisterSensor(this);
}
void SCA_ISensor::Replace_EventManager(class SCA_LogicManager* logicmgr)
{
if (m_links) { /* true if we're used currently */
m_eventmgr->RemoveSensor(this);
m_eventmgr= logicmgr->FindEventManager(m_eventmgr->GetType());
m_eventmgr->RegisterSensor(this);
}
else {
m_eventmgr= logicmgr->FindEventManager(m_eventmgr->GetType());
}
}
void SCA_ISensor::LinkToController(SCA_IController* controller)
{
m_linkedcontrollers.push_back(controller);
}
void SCA_ISensor::UnlinkController(SCA_IController* controller)
{
std::vector<class SCA_IController*>::iterator contit;
for (contit = m_linkedcontrollers.begin();!(contit==m_linkedcontrollers.end());++contit)
{
if ((*contit) == controller)
{
*contit = m_linkedcontrollers.back();
m_linkedcontrollers.pop_back();
return;
}
}
printf("Missing link from sensor %s:%s to controller %s:%s\n",
m_gameobj->GetName().ReadPtr(), GetName().ReadPtr(),
controller->GetParent()->GetName().ReadPtr(), controller->GetName().ReadPtr());
}
void SCA_ISensor::UnlinkAllControllers()
{
std::vector<class SCA_IController*>::iterator contit;
for (contit = m_linkedcontrollers.begin();!(contit==m_linkedcontrollers.end());++contit)
{
(*contit)->UnlinkSensor(this);
}
m_linkedcontrollers.clear();
}
void SCA_ISensor::UnregisterToManager()
{
m_eventmgr->RemoveSensor(this);
m_links = 0;
}
void SCA_ISensor::ActivateControllers(class SCA_LogicManager* logicmgr)
{
for (vector<SCA_IController*>::const_iterator c= m_linkedcontrollers.begin();
c!=m_linkedcontrollers.end();++c)
{
SCA_IController* contr = *c;
if (contr->IsActive())
logicmgr->AddTriggeredController(contr, this);
}
}
void SCA_ISensor::Activate(class SCA_LogicManager* logicmgr)
{
// calculate if a __triggering__ is wanted
// don't evaluate a sensor that is not connected to any controller
if (m_links && !m_suspended) {
bool result = this->Evaluate();
// store the state for the rest of the logic system
m_prev_state = m_state;
m_state = this->IsPositiveTrigger();
if (result) {
// the sensor triggered this frame
if (m_state || !m_tap) {
ActivateControllers(logicmgr);
// reset these counters so that pulse are synchronized with transition
m_pos_ticks = 0;
m_neg_ticks = 0;
} else
{
result = false;
}
} else
{
/* First, the pulsing behavior, if pulse mode is
* active. It seems something goes wrong if pulse mode is
* not set :( */
if (m_pos_pulsemode) {
m_pos_ticks++;
if (m_pos_ticks > m_skipped_ticks) {
if ( m_state )
{
ActivateControllers(logicmgr);
result = true;
}
m_pos_ticks = 0;
}
}
// negative pulse doesn't make sense in tap mode, skip
if (m_neg_pulsemode && !m_tap)
{
m_neg_ticks++;
if (m_neg_ticks > m_skipped_ticks) {
if (!m_state )
{
ActivateControllers(logicmgr);
result = true;
}
m_neg_ticks = 0;
}
}
}
if (m_tap)
{
// in tap mode: we send always a negative pulse immediately after a positive pulse
if (!result)
{
// the sensor did not trigger on this frame
if (m_prev_state)
{
// but it triggered on previous frame => send a negative pulse
ActivateControllers(logicmgr);
result = true;
}
// in any case, absence of trigger means sensor off
m_state = false;
}
}
if (!result && m_level)
{
// This level sensor is connected to at least one controller that was just made
// active but it did not generate an event yet, do it now to those controllers only
for (vector<SCA_IController*>::const_iterator c= m_linkedcontrollers.begin();
c!=m_linkedcontrollers.end();++c)
{
SCA_IController* contr = *c;
if (contr->IsJustActivated())
logicmgr->AddTriggeredController(contr, this);
}
}
}
}
#ifdef WITH_PYTHON
/* ----------------------------------------------- */
/* Python Functions */
/* ----------------------------------------------- */
KX_PYMETHODDEF_DOC_NOARGS(SCA_ISensor, reset,
"reset()\n"
"\tReset sensor internal state, effect depends on the type of sensor and settings.\n"
"\tThe sensor is put in its initial state as if it was just activated.\n")
{
Init();
m_prev_state = false;
Py_RETURN_NONE;
}
/* ----------------------------------------------- */
/* Python Integration Hooks */
/* ----------------------------------------------- */
PyTypeObject SCA_ISensor::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"SCA_ISensor",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,0,0,0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&SCA_ILogicBrick::Type,
0,0,0,0,0,0,
py_base_new
};
PyMethodDef SCA_ISensor::Methods[] = {
KX_PYMETHODTABLE_NOARGS(SCA_ISensor, reset),
{NULL,NULL} //Sentinel
};
PyAttributeDef SCA_ISensor::Attributes[] = {
KX_PYATTRIBUTE_BOOL_RW("usePosPulseMode",SCA_ISensor,m_pos_pulsemode),
KX_PYATTRIBUTE_BOOL_RW("useNegPulseMode",SCA_ISensor,m_neg_pulsemode),
KX_PYATTRIBUTE_INT_RW("skippedTicks",0,100000,true,SCA_ISensor,m_skipped_ticks),
KX_PYATTRIBUTE_BOOL_RW("invert",SCA_ISensor,m_invert),
KX_PYATTRIBUTE_BOOL_RW_CHECK("level",SCA_ISensor,m_level,pyattr_check_level),
KX_PYATTRIBUTE_BOOL_RW_CHECK("tap",SCA_ISensor,m_tap,pyattr_check_tap),
KX_PYATTRIBUTE_RO_FUNCTION("triggered", SCA_ISensor, pyattr_get_triggered),
KX_PYATTRIBUTE_RO_FUNCTION("positive", SCA_ISensor, pyattr_get_positive),
KX_PYATTRIBUTE_RO_FUNCTION("status", SCA_ISensor, pyattr_get_status),
KX_PYATTRIBUTE_RO_FUNCTION("pos_ticks", SCA_ISensor, pyattr_get_posTicks),
KX_PYATTRIBUTE_RO_FUNCTION("neg_ticks", SCA_ISensor, pyattr_get_negTicks),
KX_PYATTRIBUTE_RW_FUNCTION("frequency", SCA_ISensor, pyattr_get_frequency, pyattr_set_frequency),
{ NULL } //Sentinel
};
PyObject *SCA_ISensor::pyattr_get_triggered(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
bool retval = false;
if (SCA_PythonController::m_sCurrentController)
retval = SCA_PythonController::m_sCurrentController->IsTriggered(self);
return PyBool_FromLong(retval);
}
PyObject *SCA_ISensor::pyattr_get_positive(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
return PyBool_FromLong(self->GetState());
}
PyObject *SCA_ISensor::pyattr_get_status(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
int status = 0;
if (self->GetState())
{
if (self->GetState() == self->GetPrevState())
{
status = 2;
}
else
{
status = 1;
}
}
else if (self->GetState() != self->GetPrevState())
{
status = 3;
}
return PyLong_FromLong(status);
}
PyObject *SCA_ISensor::pyattr_get_posTicks(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
return PyLong_FromLong(self->GetPosTicks());
}
PyObject *SCA_ISensor::pyattr_get_negTicks(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
return PyLong_FromLong(self->GetNegTicks());
}
int SCA_ISensor::pyattr_check_level(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
if (self->m_level)
self->m_tap = false;
return 0;
}
int SCA_ISensor::pyattr_check_tap(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor* self = static_cast<SCA_ISensor*>(self_v);
if (self->m_tap)
self->m_level = false;
return 0;
}
PyObject *SCA_ISensor::pyattr_get_frequency(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
SCA_ISensor *self = static_cast<SCA_ISensor*>(self_v);
ShowDeprecationWarning("SCA_ISensor.frequency", "SCA_ISensor.skippedTicks");
return PyLong_FromLong(self->m_skipped_ticks);
}
int SCA_ISensor::pyattr_set_frequency(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
SCA_ISensor *self = static_cast<SCA_ISensor*>(self_v);
ShowDeprecationWarning("SCA_ISensor.frequency", "SCA_ISensor.skippedTicks");
if (PyLong_Check(value)) {
self->m_skipped_ticks = PyLong_AsLong(value);
return PY_SET_ATTR_SUCCESS;
}
else {
PyErr_SetString(PyExc_TypeError, "sensor.frequency = int: Sensor, expected an integer");
return PY_SET_ATTR_FAIL;
}
}
#endif // WITH_PYTHON
/* eof */